Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.adorsys.multibanking.bg.BankingGatewayAdapter Maven / Gradle / Ivy
package de.adorsys.multibanking.bg;
import com.google.gson.JsonSyntaxException;
import com.squareup.okhttp.Call;
import de.adorsys.multibanking.domain.*;
import de.adorsys.multibanking.domain.exception.MultibankingError;
import de.adorsys.multibanking.domain.exception.MultibankingException;
import de.adorsys.multibanking.domain.request.TransactionRequest;
import de.adorsys.multibanking.domain.response.*;
import de.adorsys.multibanking.domain.spi.OnlineBankingService;
import de.adorsys.multibanking.domain.spi.StrongCustomerAuthorisable;
import de.adorsys.multibanking.domain.transaction.*;
import de.adorsys.multibanking.mapper.TransactionsParser;
import de.adorsys.multibanking.xs2a_adapter.ApiException;
import de.adorsys.multibanking.xs2a_adapter.ApiResponse;
import de.adorsys.multibanking.xs2a_adapter.api.AccountInformationServiceAisApi;
import de.adorsys.multibanking.xs2a_adapter.model.*;
import de.adorsys.multibanking.xs2a_adapter.model.Balance;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.json.XML;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import static de.adorsys.multibanking.bg.ApiClientFactory.accountInformationServiceAisApi;
import static de.adorsys.multibanking.domain.BankApi.XS2A;
import static de.adorsys.multibanking.domain.exception.MultibankingError.*;
@Slf4j
public class BankingGatewayAdapter implements OnlineBankingService {
private final BankingGatewayScaHandler scaHandler;
private final String xs2aAdapterBaseUrl;
private BankingGatewayMapper bankingGatewayMapper = new BankingGatewayMapperImpl();
public BankingGatewayAdapter(String bankingGatewayBaseUrl, String xs2aAdapterBaseUrl) {
this.scaHandler = new BankingGatewayScaHandler(bankingGatewayBaseUrl);
this.xs2aAdapterBaseUrl = xs2aAdapterBaseUrl;
}
@Override
public BankApi bankApi() {
return XS2A;
}
@Override
public boolean externalBankAccountRequired() {
return false;
}
@Override
public boolean userRegistrationRequired() {
return true;
}
@Override
public BankApiUser registerUser(String userId) {
BankApiUser bankApiUser = new BankApiUser();
bankApiUser.setBankApi(bankApi());
return bankApiUser;
}
@Override
public void removeUser(BankApiUser bankApiUser) {
//noop
}
@Override
public AccountInformationResponse loadBankAccounts(TransactionRequest loadAccountInformationRequest) {
try {
String bankCode = loadAccountInformationRequest.getBank().getBankApiBankCode() != null
? loadAccountInformationRequest.getBank().getBankApiBankCode()
: loadAccountInformationRequest.getBankAccess().getBankCode();
BgSessionData bgSessionData = (BgSessionData) loadAccountInformationRequest.getBankApiConsentData();
AccountList accountList = getAccountList(bgSessionData, bankCode,
loadAccountInformationRequest.getBankAccess().getConsentId());
List bankAccounts = bankingGatewayMapper.toBankAccounts(accountList.getAccounts());
return AccountInformationResponse.builder()
.bankAccess(loadAccountInformationRequest.getBankAccess())
.bankAccounts(bankAccounts)
.build();
} catch (ApiException e) {
throw handeAisApiException(e);
}
}
private AccountList getAccountList(BgSessionData bgSessionData, String bankCode, String consentId) throws ApiException {
AccountInformationServiceAisApi aisApi = accountInformationServiceAisApi(xs2aAdapterBaseUrl, bgSessionData);
return aisApi.getAccountList(UUID.randomUUID(), consentId, null, bankCode, null, false, null, null,
null, null,
null, null, null, null, null, null, null, null, null);
}
@Override
public void removeBankAccount(BankAccount bankAccount, BankApiUser bankApiUser) {
//noop
}
public TransactionsResponse loadTransactions(TransactionRequest loadTransactionsRequest) {
LoadTransactions loadTransactions = loadTransactionsRequest.getTransaction();
LocalDate dateFrom = loadTransactions.getDateFrom() != null ? loadTransactions.getDateFrom() :
LocalDate.now().minusYears(1);
String bankCode = loadTransactionsRequest.getBank().getBankApiBankCode() != null
? loadTransactionsRequest.getBank().getBankApiBankCode()
: loadTransactionsRequest.getBankAccess().getBankCode();
BgSessionData bgSessionData = (BgSessionData) loadTransactionsRequest.getBankApiConsentData();
try {
String resourceId = Optional.ofNullable(loadTransactions.getPsuAccount().getExternalIdMap().get(bankApi()))
.orElseGet(() -> getAccountResourceId(bgSessionData,
loadTransactionsRequest.getBankAccess().getIban(), bankCode,
loadTransactionsRequest.getBankAccess().getConsentId()));
AccountInformationServiceAisApi aisApi = accountInformationServiceAisApi(xs2aAdapterBaseUrl,
bgSessionData);
Call aisCall = aisApi.getTransactionListCall(
resourceId, "booked", UUID.randomUUID(),
loadTransactionsRequest.getBankAccess().getConsentId(), null, bankCode, null, dateFrom,
loadTransactions.getDateTo(), null,
null, loadTransactions.isWithBalance(), null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null);
ApiResponse apiResponse = aisApi.getApiClient().execute(aisCall, String.class);
String contentTypeKey = apiResponse.getHeaders().keySet().stream()
.filter(header -> header.toLowerCase().contains("content-type"))
.findFirst()
.orElse("");
String contentType = Optional.ofNullable(apiResponse.getHeaders().get(contentTypeKey))
.map(list -> list.get(0))
.orElse("");
if (contentType.toLowerCase().contains("application/xml")) {
return TransactionsParser.camtStringToLoadBookingsResponse((String) apiResponse.getData());
} else if (contentType.toLowerCase().contains("text/plain")) {
return TransactionsParser.mt940StringToLoadBookingsResponse((String) apiResponse.getData());
} else {
return jsonStringToLoadBookingsResponse((String) apiResponse.getData());
}
} catch (ApiException e) {
throw handeAisApiException(e);
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new MultibankingException(INTERNAL_ERROR, 500, "Error loading bookings: " + e.getMessage());
}
}
@Override
public StandingOrdersResponse loadStandingOrders(TransactionRequest loadStandingOrdersRequest) {
throw new UnsupportedOperationException();
}
@Override
public LoadBalancesResponse loadBalances(TransactionRequest request) {
throw new UnsupportedOperationException();
}
public TransactionsResponse jsonStringToLoadBookingsResponse(String json) {
TransactionsResponse200Json transactionsResponse200JsonTO =
GsonConfig.getGson().fromJson(json, TransactionsResponse200Json.class);
List bookings = Optional.ofNullable(transactionsResponse200JsonTO)
.map(TransactionsResponse200Json::getTransactions)
.map(AccountReport::getBooked)
.map(transactions -> bankingGatewayMapper.toBookings(transactions))
.orElse(Collections.emptyList());
BalancesReport balancesReport = new BalancesReport();
Optional.ofNullable(transactionsResponse200JsonTO)
.map(TransactionsResponse200Json::getBalances)
.map(List::stream).orElse(Stream.empty())
.filter(balance -> balance.getBalanceType() != null)
.forEach(balance -> {
switch (balance.getBalanceType()) {
case EXPECTED:
balancesReport.setUnreadyBalance(bankingGatewayMapper.toBalance(balance));
break;
case CLOSINGBOOKED:
balancesReport.setReadyBalance(bankingGatewayMapper.toBalance(balance));
break;
default:
// ignore
break;
}
});
Optional.ofNullable(transactionsResponse200JsonTO)
.map(TransactionsResponse200Json::getBalances)
.map(List::stream).orElse(Stream.empty())
.filter(balance -> BalanceType.OPENINGBOOKED.equals(balance.getBalanceType()))
.findFirst().ifPresent(
openingbooked -> {
BigDecimal balance = new BigDecimal(openingbooked.getBalanceAmount().getAmount());
for(Booking booking : bookings) {
balance = balance.add(booking.getAmount());
booking.setBalance(balance);
}
Optional.ofNullable(balancesReport.getReadyBalance()).ifPresent(
readyBalance -> {
BigDecimal lastBookingBalance = bookings.get(bookings.size() - 1).getBalance();
if (!readyBalance.getAmount().equals(lastBookingBalance)) {
log.error("The closing booked balance {} and the calculated balance after the last transaction {} are not equal", readyBalance.getAmount(), lastBookingBalance);
}
}
);
}
);
return TransactionsResponse.builder()
.bookings(bookings)
.balancesReport(balancesReport)
.build();
}
private String getAccountResourceId(BgSessionData bgSessionData, String iban, String bankCode, String consentId) {
try {
AccountList accountList = getAccountList(bgSessionData, bankCode, consentId);
return accountList.getAccounts()
.stream()
.filter(accountDetails -> accountDetails.getIban().equals(iban))
.findAny()
.map(AccountDetails::getResourceId)
.orElseThrow(() -> new MultibankingException(INVALID_ACCOUNT_REFERENCE));
} catch (ApiException e) {
throw handeAisApiException(e);
}
}
@Override
public boolean bankSupported(String bankCode) {
return true;
}
@Override
public boolean bookingsCategorized() {
return false;
}
@Override
public PaymentResponse executePayment(TransactionRequest extends AbstractPayment> paymentRequest) {
throw new UnsupportedOperationException();
}
@Override
public StrongCustomerAuthorisable getStrongCustomerAuthorisation() {
return scaHandler;
}
private MultibankingException handeAisApiException(ApiException e) {
switch (e.getCode()) {
case 401:
return toMultibankingException(e, INVALID_PIN);
case 404:
return toMultibankingException(e, RESOURCE_NOT_FOUND);
case 429:
return new MultibankingException(INVALID_CONSENT, 429, "consent access exceeded");
default:
return toMultibankingException(e, BANKING_GATEWAY_ERROR);
}
}
private MultibankingException toMultibankingException(ApiException e, MultibankingError multibankingError) {
try {
Error400NGAIS messagesTO = GsonConfig.getGson().fromJson(e.getResponseBody(),
Error400NGAIS.class);
return new MultibankingException(multibankingError, e.getCode(), null,
bankingGatewayMapper.toMessagesFromTppMessage400AIS(messagesTO.getTppMessages()));
} catch (JsonSyntaxException jpe) {
// try xml
TppMessage400AIS messageTO = Optional.ofNullable(e.getResponseBody())
.filter(xml -> xml.startsWith("<"))
.map(uncheckFunction(XML::toJSONObject))
.map(BankingGatewayAdapter::findTppMessage)
.map(uncheckFunction(message -> GsonConfig.getGson().fromJson(message.toString(), TppMessage400AIS.class)))
.orElse(null);
if (messageTO != null) {
return new MultibankingException(multibankingError, e.getCode(), null,
Collections.singletonList(bankingGatewayMapper.toMessage(messageTO)));
} else {
return new MultibankingException(multibankingError, 500, e.getMessage());
}
} catch (Exception e2) {
return new MultibankingException(multibankingError, 500, e.getMessage());
}
}
private static JSONObject findTppMessage(JSONObject jsonObject) {
JSONObject message = null;
for (Iterator key = jsonObject.keys(); key.hasNext(); ) {
String nextKey = (String) key.next();
Object next = jsonObject.get(nextKey);
if ("tppMessages".equals(nextKey)) {
message = (JSONObject) next;
}
if (next instanceof JSONObject) {
JSONObject override = findTppMessage((JSONObject) next);
message = override != null ? override : message;
}
}
return message;
}
private static Function uncheckFunction(CheckedFunction throwingFunction) {
return i -> {
try {
return throwingFunction.apply(i);
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
};
}
@FunctionalInterface
public interface CheckedFunction {
R apply(T t) throws IOException;
}
}