mirror of
https://github.com/drmartinberger/FueliFinTS.git
synced 2026-08-13 12:33:22 +02:00
Added functionality to fetch account statement as MT940 from LNBits wallet transactions
This commit is contained in:
parent
c4bac38036
commit
508c05da22
9 changed files with 303 additions and 17 deletions
|
|
@ -9,7 +9,7 @@ Fueli FinTS is a server implementing the financial transaction services (FinTS)
|
|||
4. Added example configuration files and db script to set up a rudimentary database.
|
||||
5. Some minor refactoring and cleanups
|
||||
|
||||
**Please find a complete list of all changes to the original implementation by this fork with this [Github Compare](https://github.com/petafuel/FueliFinTS/compare/main...drmartinberger:main) view.**
|
||||
**Please find a complete list of all changes to the original implementation by this fork with this [Github Compare](https://github.com/petafuel/FueliFinTS/compare/main...drmartinberger:lnbits-via-mt940) view.**
|
||||
|
||||
# Setup Steps
|
||||
|
||||
|
|
|
|||
4
config/lnbits.properties.example
Normal file
4
config/lnbits.properties.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
lnbitsUrl = <url to LNBits instance>
|
||||
uriWallet = /api/v1/wallet
|
||||
uriPayments = /api/v1/payments
|
||||
xApiKey = <x-api-key of LNBits wallet>
|
||||
5
pom.xml
5
pom.xml
|
|
@ -119,6 +119,11 @@
|
|||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.prowidesoftware</groupId>
|
||||
<artifactId>pw-swift-core</artifactId>
|
||||
<version>SRU2022-9.3.12</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
|
|
|
|||
64
src/main/java/lnbits/LnTransaction.java
Normal file
64
src/main/java/lnbits/LnTransaction.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package lnbits;
|
||||
|
||||
import com.prowidesoftware.swift.model.field.Field61;
|
||||
import com.prowidesoftware.swift.model.field.Field86;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class LnTransaction {
|
||||
public LnTransaction() { super(); }
|
||||
|
||||
public Field61 toField61() {
|
||||
// id: max 16 chars
|
||||
String id = StringUtils.left(checkingId, 7) + "..." + StringUtils.right(checkingId, 6);
|
||||
return new Field61()
|
||||
.setValueDate(new SimpleDateFormat("yyMMdd").format(getTime()))
|
||||
.setDebitCreditMark(this.amount >= 0 ? "C" : "D")
|
||||
.setAmount(Math.abs(this.amount+this.fee)/1000L)
|
||||
.setTransactionType("NMSC")
|
||||
.setIdentificationCode(id);
|
||||
}
|
||||
|
||||
public Field86 toField86(){
|
||||
return new Field86().setComponent1(this.memo);
|
||||
}
|
||||
|
||||
public Long getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public Long getFee() {
|
||||
return fee;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return new Date(this.time*1000);
|
||||
}
|
||||
|
||||
public Boolean getPending() { return pending; }
|
||||
|
||||
@JsonProperty("amount")
|
||||
private Long amount;
|
||||
@JsonProperty("fee")
|
||||
private Long fee;
|
||||
|
||||
@JsonProperty("memo")
|
||||
private String memo;
|
||||
|
||||
// time in seconds since epoch
|
||||
@JsonProperty("time")
|
||||
private Long time;
|
||||
|
||||
@JsonProperty("checking_id")
|
||||
private String checkingId;
|
||||
|
||||
@JsonProperty("pending")
|
||||
private Boolean pending;
|
||||
|
||||
}
|
||||
150
src/main/java/lnbits/LnbitsHelper.java
Normal file
150
src/main/java/lnbits/LnbitsHelper.java
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package lnbits;
|
||||
|
||||
import com.prowidesoftware.swift.model.field.*;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.type.TypeReference;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LnbitsHelper {
|
||||
private static String lnbitsUrl;
|
||||
|
||||
private static String uriWallet;
|
||||
private static String uriPayments;
|
||||
|
||||
private static String xApiKey;
|
||||
|
||||
private static void loadConfiguration() {
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
BufferedInputStream stream = new BufferedInputStream(new FileInputStream("config/lnbits.properties"));
|
||||
properties.load(stream);
|
||||
stream.close();
|
||||
|
||||
lnbitsUrl = properties.getProperty("lnbitsUrl");
|
||||
uriWallet = properties.getProperty("uriWallet");
|
||||
uriPayments = properties.getProperty("uriPayments");
|
||||
xApiKey = properties.getProperty("xApiKey");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public static Collection<Field> getMt940Fields(java.util.Date from, java.util.Date to) throws IOException {
|
||||
loadConfiguration();
|
||||
|
||||
Collection<Field> mt940Fields = new ArrayList<>();
|
||||
Long currentBalance = getCurrentBalance();
|
||||
Collection<LnTransaction> afterFromSettledPayments = getPayments()
|
||||
.stream()
|
||||
.filter(tx -> tx.getTime().after(from) && !tx.getPending())
|
||||
.collect(Collectors.toList());
|
||||
Long fromBalance = currentBalance - afterFromSettledPayments
|
||||
.stream()
|
||||
.map(tx -> tx.getAmount() + tx.getFee())
|
||||
.reduce(0L, Long::sum);
|
||||
Collection<LnTransaction> fromToPayments = afterFromSettledPayments
|
||||
.stream()
|
||||
.filter(tx -> tx.getTime().before(to))
|
||||
.collect(Collectors.toList());
|
||||
Long toBalance = fromBalance + fromToPayments
|
||||
.stream()
|
||||
.map(tx -> tx.getAmount() + tx.getFee())
|
||||
.reduce(0L, Long::sum);
|
||||
// initialBalance
|
||||
String simpleFrom = new SimpleDateFormat("yyMMdd").format(from);
|
||||
mt940Fields.add(new Field60F()
|
||||
.setDCMark("C")
|
||||
.setDate(simpleFrom)
|
||||
.setCurrency("EUR")
|
||||
.setAmount(fromBalance/1000L)
|
||||
);
|
||||
|
||||
for (LnTransaction tx : fromToPayments) {
|
||||
mt940Fields.add(tx.toField61());
|
||||
mt940Fields.add(tx.toField86());
|
||||
}
|
||||
|
||||
// finalBalance
|
||||
String simpleTo = new SimpleDateFormat("yyMMdd").format(from);
|
||||
mt940Fields.add(new Field62F()
|
||||
.setDCMark("C")
|
||||
.setDate(simpleTo)
|
||||
.setCurrency("EUR")
|
||||
.setAmount(toBalance/1000L)
|
||||
);
|
||||
|
||||
return mt940Fields;
|
||||
}
|
||||
|
||||
private static Collection<LnTransaction> getPayments() throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
try {
|
||||
HttpGet request = new HttpGet(lnbitsUrl + uriPayments);
|
||||
request.addHeader("X-API-KEY", xApiKey);
|
||||
request.addHeader(HttpHeaders.ACCEPT, "application/json");
|
||||
|
||||
CloseableHttpResponse response = httpClient.execute(request);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null && response.getStatusLine().getStatusCode() == 200) {
|
||||
String result = EntityUtils.toString(entity);
|
||||
return new ObjectMapper().readValue(result, new TypeReference<List<LnTransaction>>(){});
|
||||
} else {
|
||||
throw new RuntimeException("Request failed");
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} catch (ClientProtocolException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
private static Long getCurrentBalance() throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
try {
|
||||
HttpGet request = new HttpGet(lnbitsUrl + uriWallet);
|
||||
request.addHeader("X-API-KEY", xApiKey);
|
||||
request.addHeader(HttpHeaders.ACCEPT, "application/json");
|
||||
|
||||
CloseableHttpResponse response = httpClient.execute(request);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null && response.getStatusLine().getStatusCode() == 200) {
|
||||
String result = EntityUtils.toString(entity);
|
||||
WalletDetails walletDetails = new ObjectMapper().readValue(result, WalletDetails.class);
|
||||
return walletDetails.getBalance();
|
||||
} else {
|
||||
throw new RuntimeException("Request failed");
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} catch (ClientProtocolException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/java/lnbits/WalletDetails.java
Normal file
16
src/main/java/lnbits/WalletDetails.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package lnbits;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class WalletDetails {
|
||||
public WalletDetails() { super(); }
|
||||
|
||||
public Long getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
@JsonProperty("balance")
|
||||
private Long balance;
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import net.petafuel.jsepa.exception.SEPAParsingException;
|
|||
import net.petafuel.jsepa.model.*;
|
||||
import net.petafuel.jsepa.util.BankDateCalculator;
|
||||
import net.petafuel.jsepa.util.SepaUtils;
|
||||
import net.petafuel.mt94x.Mt940Helper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
|
@ -1311,7 +1312,11 @@ public class Banking2Access implements DataAccessFacade {
|
|||
*/
|
||||
@Override
|
||||
public byte[] getGebuchteUmsaetze(String kontonummer, Date vonDatum, Date bisDatum, LegitimationInfo legitimationsInfo) {
|
||||
return database.getGebuchteUmsaetze(kontonummer, vonDatum, bisDatum, legitimationsInfo);
|
||||
try {
|
||||
return Mt940Helper.getMT940s(kontonummer, vonDatum, bisDatum);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
37
src/main/java/net/petafuel/mt94x/Mt940Helper.java
Normal file
37
src/main/java/net/petafuel/mt94x/Mt940Helper.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package net.petafuel.mt94x;
|
||||
|
||||
import com.prowidesoftware.swift.model.BIC;
|
||||
import com.prowidesoftware.swift.model.field.*;
|
||||
import com.prowidesoftware.swift.model.mt.mt9xx.MT940;
|
||||
import lnbits.LnbitsHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class Mt940Helper {
|
||||
public static byte[] getMT940s(String kontonummer, java.util.Date vonDatum, java.util.Date bisDatum) throws IOException {
|
||||
if(vonDatum == null) {
|
||||
vonDatum = new Date(0);
|
||||
}
|
||||
if(bisDatum == null) {
|
||||
bisDatum = new Date();
|
||||
}
|
||||
return getMT940(kontonummer, vonDatum, bisDatum).getBytes();
|
||||
}
|
||||
|
||||
private static String getMT940(String kontonummer, java.util.Date vonDatum, java.util.Date bisDatum) throws IOException {
|
||||
String simpleDate = new SimpleDateFormat("yyMMdd").format(bisDatum);
|
||||
MT940 a = new MT940();
|
||||
a.setSender(new BIC("NOOODE00BIC"));
|
||||
a.setReceiver(new BIC("NOOODE00BIC"));
|
||||
a.addField(new Field20(simpleDate)); // Transaktionsnummer
|
||||
a.addField(new Field21("NONREF")); // Bezugsreferenznummer
|
||||
a.addField(new Field25(kontonummer)); // Kontobezeichnung
|
||||
a.addField(new Field28C((simpleDate))); // Auszugsnummer
|
||||
for(Field f : LnbitsHelper.getMt940Fields(vonDatum, bisDatum)) {
|
||||
a.addField(f);
|
||||
}
|
||||
return a.message();
|
||||
}
|
||||
}
|
||||
35
third_party/README.md
vendored
35
third_party/README.md
vendored
|
|
@ -1,6 +1,6 @@
|
|||
# Third Party Licenses
|
||||
|
||||
Fueli FinTS is build on top of other open source projects. Their licence files are included in the source code's 'third_party' folder. The third party software included and/or used by Fueli FinTS is: (in alphabetical order)
|
||||
This fork of Fueli FinTS is build on top of other open source projects. Their licence files are included in the source code's 'third_party' folder. The third party software included and/or used by Fueli FinTS is: (in alphabetical order)
|
||||
|
||||
- Apache Commons Lang
|
||||
Copyright 2001-2021 The Apache Software Foundation
|
||||
|
|
@ -58,27 +58,32 @@ Fueli FinTS is build on top of other open source projects. Their licence files a
|
|||
- MySQL Connector/J
|
||||
Copyright (c) 1998, 2021 Oracle and/or its affiliates.
|
||||
GNU General Public License Version 2.0
|
||||
https://dev.mysql.com/doc/connector-j/5.1/en/
|
||||
https://dev.mysql.com/doc/connector-j/8.0/en/
|
||||
|
||||
- OWASP Dependency-Check
|
||||
Copyright (c) 2012-2021 Jeremy Long. All Rights Reserved.
|
||||
Apache-2.0 License
|
||||
https://owasp.org/www-project-dependency-check/
|
||||
|
||||
- petaFuel DBUtils
|
||||
Copyright 2021 petaFuel GmbH
|
||||
DBUtils Software License Agreement
|
||||
https://github.com/petafuel
|
||||
# The following commented libraries are no longer publicly available:
|
||||
[//]: # (- petaFuel DBUtils )
|
||||
[//]: # ( Copyright 2021 petaFuel GmbH )
|
||||
[//]: # ( DBUtils Software License Agreement )
|
||||
[//]: # ( https://github.com/petafuel)
|
||||
[//]: # ()
|
||||
[//]: # (- petaFuel JSEPA )
|
||||
[//]: # ( Copyright 2021 petaFuel GmbH )
|
||||
[//]: # ( JSEPA Software License Agreement )
|
||||
[//]: # ( https://github.com/petafuel)
|
||||
[//]: # ()
|
||||
[//]: # (- petaFuel Mt94x )
|
||||
[//]: # ( Copyright 2021 petaFuel GmbH )
|
||||
[//]: # ( Mt94x Software License Agreement )
|
||||
[//]: # ( https://github.com/petafuel)
|
||||
|
||||
- petaFuel JSEPA
|
||||
Copyright 2021 petaFuel GmbH
|
||||
JSEPA Software License Agreement
|
||||
https://github.com/petafuel
|
||||
|
||||
- petaFuel Mt94x
|
||||
Copyright 2021 petaFuel GmbH
|
||||
Mt94x Software License Agreement
|
||||
https://github.com/petafuel
|
||||
- Prowide Core
|
||||
Apache-2.0 License
|
||||
https://www.prowidesoftware.com/products/core
|
||||
|
||||
- RESTEasy
|
||||
Apache-2.0 License
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue