korrektur
This commit is contained in:
parent
11b28ef9c3
commit
bb058306f0
7 changed files with 370 additions and 189 deletions
76
doc/KSeF/KSEF_CLIENT.md
Normal file
76
doc/KSeF/KSEF_CLIENT.md
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# ksef-client
|
||||||
|
|
||||||
|
Um die ksef-client Library nutzen zu können muss man eine relativ komplexe prozedur durchlaufen um Zugrifff darauf zu erhalen:
|
||||||
|
|
||||||
|
## 1. Personal Access Token (PAT) erstellen
|
||||||
|
|
||||||
|
Du brauchst einen GitHub-Token, damit dein Projekt das Paket aus GitHub Packages herunterladen darf.
|
||||||
|
|
||||||
|
So machst du das:
|
||||||
|
|
||||||
|
1. Klicke oben rechts auf GitHub auf dein Profil.
|
||||||
|
2. Gehe zu Settings.
|
||||||
|
3. Links unten: Developer settings.
|
||||||
|
4. Dann: Personal access tokens.
|
||||||
|
5. Dann: Tokens (classic).
|
||||||
|
6. Klicke: Generate new token (classic).
|
||||||
|
|
||||||
|
**Beim Erstellen:**
|
||||||
|
|
||||||
|
- Vergib irgendeinen Namen (z. B. „ksef-client access“).
|
||||||
|
- Wähle nur ein einziges Häkchen:
|
||||||
|
|
||||||
|
👉 read:packages
|
||||||
|
|
||||||
|
- Token generieren → Token kopieren! Du siehst ihn nur einmal.
|
||||||
|
|
||||||
|
## 2. Maven für GitHub Packages konfigurieren
|
||||||
|
|
||||||
|
Damit Maven die Pakete laden kann, musst du GitHub Packages in der Datei `~/.m2/settings.xml` eintragen.
|
||||||
|
|
||||||
|
👉 Inhalt, den du einfügen musst:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
|
||||||
|
https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||||
|
<servers>
|
||||||
|
<server>
|
||||||
|
<id>github-cirfmf</id>
|
||||||
|
<username>rsoika</username>
|
||||||
|
<password>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</password>
|
||||||
|
</server>
|
||||||
|
|
||||||
|
</servers>
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Repository in deiner pom.xml eintragen
|
||||||
|
|
||||||
|
Füge das in deine pom.xml ein:
|
||||||
|
|
||||||
|
```
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>github-cirfmf</id>
|
||||||
|
<url>https://maven.pkg.github.com/CIRFMF/ksef-client-java</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Abhängigkeit hinzufügen
|
||||||
|
|
||||||
|
Auch in die pom.xml:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
..
|
||||||
|
<dependency>
|
||||||
|
<groupId>pl.akmf.ksef-sdk</groupId>
|
||||||
|
<artifactId>ksef-client</artifactId>
|
||||||
|
<version>3.0.4</version>
|
||||||
|
</dependency>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
@ -400,12 +400,18 @@
|
||||||
|
|
||||||
<!-- KSeF 2.0 Client -->
|
<!-- KSeF 2.0 Client -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.alapierre.ksef-sdk</groupId>
|
<groupId>pl.akmf.ksef-sdk</groupId>
|
||||||
<artifactId>ksef-client</artifactId>
|
<artifactId>ksef-client</artifactId>
|
||||||
<version>2.1.8</version>
|
<version>3.0.4</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>github-cirfmf</id>
|
||||||
|
<url>https://maven.pkg.github.com/CIRFMF/ksef-client-java</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -7,7 +7,11 @@ import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
|
|
@ -18,6 +22,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import org.imixs.workflow.FileData;
|
import org.imixs.workflow.FileData;
|
||||||
import org.imixs.workflow.ItemCollection;
|
import org.imixs.workflow.ItemCollection;
|
||||||
|
import org.imixs.workflow.engine.DocumentService;
|
||||||
import org.imixs.workflow.exceptions.PluginException;
|
import org.imixs.workflow.exceptions.PluginException;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|
@ -36,172 +41,185 @@ import jakarta.json.bind.JsonbBuilder;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
|
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
|
||||||
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
|
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
|
||||||
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
|
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
|
||||||
@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
|
@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
|
||||||
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
|
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
|
||||||
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
|
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
|
||||||
@Singleton
|
@Singleton
|
||||||
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
|
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
|
||||||
public class KSeFAPIService {
|
public class KSeFAPIService {
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(KSeFAPIService.class.getName());
|
private static Logger logger = Logger.getLogger(KSeFAPIService.class.getName());
|
||||||
|
|
||||||
public static final String ERROR_API = "API_ERROR";
|
public static final String ERROR_API = "API_ERROR";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
KSeFAuthManager kseFAuthManager;
|
KSeFAuthManager kseFAuthManager;
|
||||||
|
|
||||||
@PostConstruct
|
@Inject
|
||||||
void init() {
|
DocumentService documentService;
|
||||||
|
|
||||||
}
|
@PostConstruct
|
||||||
|
void init() {
|
||||||
|
|
||||||
/**
|
|
||||||
* This method uploads a KSeF Invoice document (XML).
|
|
||||||
* The response of the upload is a 'referenceNumber' which is stored into the
|
|
||||||
* item ksef.referenceNumber
|
|
||||||
*
|
|
||||||
* @param workitem - the workitem containing the invoice file
|
|
||||||
* @param fileName - the name of the invoice file to upload
|
|
||||||
* @return referenceNumber - the KSeF reference number for the uploaded invoice
|
|
||||||
* @throws PluginException
|
|
||||||
*/
|
|
||||||
public String uploadInvoice(ItemCollection workitem, String fileName)
|
|
||||||
throws PluginException {
|
|
||||||
String referenceNumber = null;
|
|
||||||
logger.info("├── Upload Invoice...");
|
|
||||||
|
|
||||||
// First open an interactive Session. The KSeFAuthManager automatically reuses
|
|
||||||
// an existing session
|
|
||||||
kseFAuthManager.openSession();
|
|
||||||
|
|
||||||
if (kseFAuthManager.getAccessToken() == null) {
|
|
||||||
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
|
|
||||||
"API Error - missing AccessToken!");
|
|
||||||
}
|
|
||||||
if (kseFAuthManager.getSessionRefNumber() == null) {
|
|
||||||
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
|
|
||||||
"API Error - missing SessionRefNumber!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get session encryption keys
|
/**
|
||||||
byte[] aesKeyBytes = kseFAuthManager.getSessionAesKey();
|
* This method uploads an KSeF Invoice document (XML).
|
||||||
byte[] ivBytes = kseFAuthManager.getSessionIv();
|
* The response of the uplaod is a 'referenceNumber' which is stored into the
|
||||||
|
* item ksef.referenceNumber
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param companyName
|
||||||
|
* @param contactType
|
||||||
|
* @param accessToken
|
||||||
|
* @return
|
||||||
|
* @throws PluginException
|
||||||
|
*/
|
||||||
|
public String uploadInvoice(ItemCollection workitem, String fileName)
|
||||||
|
throws PluginException {
|
||||||
|
String referenceNumber = null;
|
||||||
|
logger.info("├── Upload Invoice...");
|
||||||
|
|
||||||
if (aesKeyBytes == null || ivBytes == null) {
|
// First open an interactive Session. The KSeFAuthManager automatically reuses
|
||||||
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
|
// an existing session
|
||||||
"API Error - missing session encryption keys!");
|
|
||||||
|
kseFAuthManager.openSession();
|
||||||
|
|
||||||
|
if (kseFAuthManager.getAccessToken() == null) {
|
||||||
|
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
|
||||||
|
"API Error - missing AccessToken!");
|
||||||
|
}
|
||||||
|
if (kseFAuthManager.getSessionRefNumber() == null) {
|
||||||
|
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
|
||||||
|
"API Error - missing SessionRefNumber!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// load invoice
|
||||||
|
FileData fileData = workitem.getFileData(fileName);
|
||||||
|
|
||||||
|
byte[] invoiceXml = fileData.getContent();
|
||||||
|
|
||||||
|
logger.info("│ ├── XML Invoice loaded - " + invoiceXml.length + " bytes");
|
||||||
|
try {
|
||||||
|
// ---------- 3) AES Key generieren ----------
|
||||||
|
logger.info("│ ├── Generate AES Key...");
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
|
byte[] aesKeyBytes = new byte[32]; // 256-bit AES key
|
||||||
|
random.nextBytes(aesKeyBytes);
|
||||||
|
|
||||||
|
byte[] ivBytes = new byte[16]; // 128-bit IV
|
||||||
|
random.nextBytes(ivBytes);
|
||||||
|
|
||||||
|
SecretKeySpec aesKey = new SecretKeySpec(aesKeyBytes, "AES");
|
||||||
|
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||||
|
|
||||||
|
// ---------- 4) XML per AES-256-CBC verschlüsseln ----------
|
||||||
|
javax.crypto.Cipher aesCipher;
|
||||||
|
|
||||||
|
aesCipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
|
|
||||||
|
aesCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, iv);
|
||||||
|
|
||||||
|
byte[] encryptedInvoiceXml = aesCipher.doFinal(invoiceXml);
|
||||||
|
|
||||||
|
// ---------- 5) AES Key per RSA verschlüsseln ----------
|
||||||
|
PublicKey ksefPublicKey = kseFAuthManager.getSymmetricPublicKey();
|
||||||
|
|
||||||
|
javax.crypto.Cipher rsaCipher = javax.crypto.Cipher
|
||||||
|
.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
|
||||||
|
rsaCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, ksefPublicKey);
|
||||||
|
|
||||||
|
byte[] encryptedAesKey = rsaCipher.doFinal(aesKeyBytes);
|
||||||
|
|
||||||
|
// ---------- 6) SHA-256 Hash des Klartext-XML berechnen ----------
|
||||||
|
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||||
|
|
||||||
|
// hash origin invoice
|
||||||
|
byte[] xmlHashBytes = sha256.digest(invoiceXml);
|
||||||
|
String invoiceHash = Base64.getEncoder().encodeToString(xmlHashBytes);
|
||||||
|
|
||||||
|
// hash encrypted invoice
|
||||||
|
xmlHashBytes = sha256.digest(encryptedInvoiceXml);
|
||||||
|
String encryptedInvoiceHash = Base64.getEncoder().encodeToString(xmlHashBytes);
|
||||||
|
|
||||||
|
// ---------- 7) Request-Body bauen ----------
|
||||||
|
// Struktur gemäß KSeF-Doku
|
||||||
|
String jsonBody = String.format("{ " +
|
||||||
|
"\"invoiceHash\": \"%s\", " +
|
||||||
|
"\"invoiceSize\": \"%s\", " +
|
||||||
|
"\"encryptedInvoiceHash\": \"%s\", " +
|
||||||
|
"\"encryptedInvoiceSize\": \"%s\", " +
|
||||||
|
"\"encryptedInvoiceContent\": \"%s\", " +
|
||||||
|
"\"offlineMode\": false " +
|
||||||
|
"}",
|
||||||
|
invoiceHash,
|
||||||
|
invoiceXml.length,
|
||||||
|
encryptedInvoiceHash,
|
||||||
|
encryptedInvoiceXml.length,
|
||||||
|
Base64.getEncoder().encodeToString(encryptedInvoiceXml));
|
||||||
|
|
||||||
|
// ---------- 8) HTTP POST vorbereiten ----------
|
||||||
|
// String uri = manager.getBaseURI() + "/sessions/online/" +
|
||||||
|
// manager.getRefNumber() + "/invoices";
|
||||||
|
String uri = kseFAuthManager.getBaseURI() + "/sessions/online/"
|
||||||
|
+ kseFAuthManager.getSessionRefNumber()
|
||||||
|
+ "/invoices";
|
||||||
|
logger.info("│ ├── Endpoint: " + uri);
|
||||||
|
|
||||||
|
if (kseFAuthManager.isDebug()) {
|
||||||
|
logger.info("Request: " + jsonBody);
|
||||||
|
}
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(uri))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Authorization", "Bearer " + kseFAuthManager.getAccessToken())
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// ---------- 9) Call ausführen ----------
|
||||||
|
var response = kseFAuthManager.getHttpClient().send(request,
|
||||||
|
java.net.http.HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
|
System.out.println("Upload Response:");
|
||||||
|
System.out.println(response.statusCode());
|
||||||
|
System.out.println(response.body());
|
||||||
|
|
||||||
|
try (Jsonb jsonb = JsonbBuilder.create()) {
|
||||||
|
// Annahme: response.body() ist ein String mit JSON-Inhalt
|
||||||
|
JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class);
|
||||||
|
referenceNumber = jsonObject.getString("referenceNumber");
|
||||||
|
|
||||||
|
if (kseFAuthManager.isDebug()) {
|
||||||
|
logger.info("│ ├── referenceNumber: " + referenceNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.severe("├── ⚠️ Error parsing JSON response" + e.getMessage());
|
||||||
|
throw new PluginException(KSeFAuthManager.class.getSimpleName(), ERROR_API,
|
||||||
|
"Error parsing JSON response: " + e.getMessage());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.statusCode() == 202) {
|
||||||
|
logger.info("├── ✅ Upload successful ");
|
||||||
|
} else {
|
||||||
|
logger.info("├── ⚠️ Upload failed! ");
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | InterruptedException
|
||||||
|
| InvalidKeyException | IllegalBlockSizeException | BadPaddingException
|
||||||
|
| InvalidAlgorithmParameterException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// finally set the referenceNumber
|
||||||
|
List<Object> textlist = new ArrayList<Object>();
|
||||||
|
textlist.add(referenceNumber);
|
||||||
|
fileData.setAttribute("ksef.referenceNumber", textlist);
|
||||||
|
|
||||||
|
return referenceNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load invoice file
|
|
||||||
FileData fileData = workitem.getFileData(fileName);
|
|
||||||
if (fileData == null) {
|
|
||||||
throw new PluginException(KSeFAPIService.class.getSimpleName(), ERROR_API,
|
|
||||||
"File not found: " + fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] invoiceXml = fileData.getContent();
|
|
||||||
logger.info("│ ├── XML Invoice loaded - " + invoiceXml.length + " bytes");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// ---------- 1) Use session AES key and IV ----------
|
|
||||||
logger.info("│ ├── Using session AES Key...");
|
|
||||||
SecretKeySpec aesKey = new SecretKeySpec(aesKeyBytes, "AES");
|
|
||||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
|
||||||
|
|
||||||
// ---------- 2) Encrypt XML with AES-256-CBC ----------
|
|
||||||
logger.info("│ ├── Encrypting invoice with AES...");
|
|
||||||
javax.crypto.Cipher aesCipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
||||||
aesCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, iv);
|
|
||||||
byte[] encryptedInvoiceXml = aesCipher.doFinal(invoiceXml);
|
|
||||||
|
|
||||||
// ---------- 3) Calculate SHA-256 hashes ----------
|
|
||||||
logger.info("│ ├── Calculating hashes...");
|
|
||||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
|
||||||
|
|
||||||
// Hash of original invoice
|
|
||||||
byte[] xmlHashBytes = sha256.digest(invoiceXml);
|
|
||||||
String invoiceHash = Base64.getEncoder().encodeToString(xmlHashBytes);
|
|
||||||
|
|
||||||
// Hash of encrypted invoice
|
|
||||||
xmlHashBytes = sha256.digest(encryptedInvoiceXml);
|
|
||||||
String encryptedInvoiceHash = Base64.getEncoder().encodeToString(xmlHashBytes);
|
|
||||||
|
|
||||||
// ---------- 4) Build request body ----------
|
|
||||||
String jsonBody = String.format("{ " +
|
|
||||||
"\"invoiceHash\": \"%s\", " +
|
|
||||||
"\"invoiceSize\": %d, " +
|
|
||||||
"\"encryptedInvoiceHash\": \"%s\", " +
|
|
||||||
"\"encryptedInvoiceSize\": %d, " +
|
|
||||||
"\"encryptedInvoiceContent\": \"%s\", " +
|
|
||||||
"\"offlineMode\": false " +
|
|
||||||
"}",
|
|
||||||
invoiceHash,
|
|
||||||
invoiceXml.length,
|
|
||||||
encryptedInvoiceHash,
|
|
||||||
encryptedInvoiceXml.length,
|
|
||||||
Base64.getEncoder().encodeToString(encryptedInvoiceXml));
|
|
||||||
|
|
||||||
// ---------- 5) Prepare HTTP POST request ----------
|
|
||||||
String uri = kseFAuthManager.getBaseURI() + "/sessions/online/"
|
|
||||||
+ kseFAuthManager.getSessionRefNumber() + "/invoices";
|
|
||||||
logger.info("│ ├── Endpoint: " + uri);
|
|
||||||
|
|
||||||
if (kseFAuthManager.isDebug()) {
|
|
||||||
logger.info("│ ├── Request body: " + jsonBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(uri))
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.header("Authorization", "Bearer " + kseFAuthManager.getAccessToken())
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// ---------- 6) Execute request ----------
|
|
||||||
logger.info("│ ├── Sending request...");
|
|
||||||
var response = kseFAuthManager.getHttpClient().send(request,
|
|
||||||
java.net.http.HttpResponse.BodyHandlers.ofString());
|
|
||||||
|
|
||||||
logger.info("│ ├── Response Code: " + response.statusCode());
|
|
||||||
|
|
||||||
if (kseFAuthManager.isDebug()) {
|
|
||||||
logger.info("│ ├── Response Body: " + response.body());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- 7) Validate response status ----------
|
|
||||||
if (response.statusCode() != 202) {
|
|
||||||
throw new PluginException(KSeFAPIService.class.getSimpleName(), ERROR_API,
|
|
||||||
"Invoice upload failed: " + response.statusCode() + " - " + response.body());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- 8) Parse response and extract reference number ----------
|
|
||||||
try (Jsonb jsonb = JsonbBuilder.create()) {
|
|
||||||
JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class);
|
|
||||||
referenceNumber = jsonObject.getString("referenceNumber");
|
|
||||||
|
|
||||||
logger.info("│ ├── Reference Number: " + referenceNumber);
|
|
||||||
logger.info("├── ✅ Invoice upload successful");
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.severe("├── ⚠️ Error parsing JSON response: " + e.getMessage());
|
|
||||||
throw new PluginException(KSeFAPIService.class.getSimpleName(), ERROR_API,
|
|
||||||
"Error parsing JSON response: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
|
||||||
| IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
|
|
||||||
logger.severe("├── ⚠️ Encryption error: " + e.getMessage());
|
|
||||||
throw new PluginException(KSeFAPIService.class.getSimpleName(), ERROR_API,
|
|
||||||
"Encryption error during invoice upload: " + e.getMessage());
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
logger.severe("├── ⚠️ HTTP request error: " + e.getMessage());
|
|
||||||
throw new PluginException(KSeFAPIService.class.getSimpleName(), ERROR_API,
|
|
||||||
"HTTP error during invoice upload: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return referenceNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,9 @@ public class KSeFAuthManager {
|
||||||
* @throws PluginException
|
* @throws PluginException
|
||||||
*/
|
*/
|
||||||
public void openSession() throws PluginException {
|
public void openSession() throws PluginException {
|
||||||
logger.info("├── open new KSeF Session...");
|
logger.info("├── open Session...");
|
||||||
if (!hasValidSession()) {
|
if (!hasValidSession()) {
|
||||||
|
logger.info("│ ├── open new interactive KSeF Session...");
|
||||||
// open new session
|
// open new session
|
||||||
this.loadPublicKeyCertificates();
|
this.loadPublicKeyCertificates();
|
||||||
|
|
||||||
|
|
@ -195,7 +196,7 @@ public class KSeFAuthManager {
|
||||||
this.redeemToken();
|
this.redeemToken();
|
||||||
this.openInteractiveSession();
|
this.openInteractiveSession();
|
||||||
} else {
|
} else {
|
||||||
logger.info("├── reuse existing KSeF Session...");
|
logger.info("│ ├── reuse existing KSeF Session...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,40 +206,44 @@ public class KSeFAuthManager {
|
||||||
* @return true if a valid session exists, false otherwise
|
* @return true if a valid session exists, false otherwise
|
||||||
*/
|
*/
|
||||||
public boolean hasValidSession() {
|
public boolean hasValidSession() {
|
||||||
// Check if session reference number exists
|
|
||||||
if (sessionRefNumber == null || sessionRefNumber.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if valid until timestamp exists
|
return false;
|
||||||
if (sessionValidUntil == null || sessionValidUntil.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
// // Check if session reference number exists
|
||||||
// Parse the validUntil timestamp (ISO 8601 format with UTC)
|
// if (sessionRefNumber == null || sessionRefNumber.isEmpty()) {
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
// return false;
|
||||||
Instant validUntilInstant = Instant.from(formatter.parse(sessionValidUntil));
|
// }
|
||||||
|
|
||||||
// Get current time in UTC
|
// // Check if valid until timestamp exists
|
||||||
Instant now = Instant.now();
|
// if (sessionValidUntil == null || sessionValidUntil.isEmpty()) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
// Check if session is still valid
|
// try {
|
||||||
boolean isValid = now.isBefore(validUntilInstant);
|
// // Parse the validUntil timestamp (ISO 8601 format with UTC)
|
||||||
|
// DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
||||||
|
// Instant validUntilInstant = Instant.from(formatter.parse(sessionValidUntil));
|
||||||
|
|
||||||
if (debug && isValid) {
|
// // Get current time in UTC
|
||||||
logger.info("├── Session is still valid until: " + sessionValidUntil);
|
// Instant now = Instant.now();
|
||||||
} else if (debug) {
|
|
||||||
logger.info("├── Session expired at: " + sessionValidUntil);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid;
|
// // Check if session is still valid
|
||||||
|
// boolean isValid = now.isBefore(validUntilInstant);
|
||||||
|
|
||||||
} catch (Exception e) {
|
// if (debug && isValid) {
|
||||||
// If parsing fails, assume session is invalid
|
// logger.info("├── Session is still valid until: " + sessionValidUntil);
|
||||||
logger.warning("├── ⚠️ Error parsing sessionValidUntil timestamp: " + e.getMessage());
|
// } else if (debug) {
|
||||||
return false;
|
// logger.info("├── Session expired at: " + sessionValidUntil);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// return isValid;
|
||||||
|
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// // If parsing fails, assume session is invalid
|
||||||
|
// logger.warning("├── ⚠️ Error parsing sessionValidUntil timestamp: " +
|
||||||
|
// e.getMessage());
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -447,7 +452,7 @@ public class KSeFAuthManager {
|
||||||
JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class);
|
JsonObject jsonObject = jsonb.fromJson(response.body(), JsonObject.class);
|
||||||
sessionRefNumber = jsonObject.getString("referenceNumber");
|
sessionRefNumber = jsonObject.getString("referenceNumber");
|
||||||
sessionValidUntil = jsonObject.getString("validUntil");
|
sessionValidUntil = jsonObject.getString("validUntil");
|
||||||
if (debug) {
|
if (true) {
|
||||||
logger.info("│ ├── Session ReferenceNumber = " + sessionRefNumber);
|
logger.info("│ ├── Session ReferenceNumber = " + sessionRefNumber);
|
||||||
logger.info("│ └── Session ValidUntil = " + sessionValidUntil);
|
logger.info("│ └── Session ValidUntil = " + sessionValidUntil);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.alexanderlogistics.ksef;
|
package com.alexanderlogistics.ksef;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
|
@ -37,13 +38,20 @@ public class KSeFAPIServiceTest {
|
||||||
manager.setKsefToken(Params.TOKEN);
|
manager.setKsefToken(Params.TOKEN);
|
||||||
manager.setKsefNip(Params.NIP);
|
manager.setKsefNip(Params.NIP);
|
||||||
manager.setKsefEndpoint("https://ksef-test.mf.gov.pl/api/v2");
|
manager.setKsefEndpoint("https://ksef-test.mf.gov.pl/api/v2");
|
||||||
manager.setDebug(false);
|
manager.setDebug(true);
|
||||||
|
|
||||||
// init() ausführen
|
// init() ausführen
|
||||||
manager.init();
|
manager.init();
|
||||||
|
|
||||||
// Open Session
|
// Open Session
|
||||||
manager.openSession();
|
manager.openSession();
|
||||||
|
|
||||||
|
// manager.loadPublicKeyCertificates();
|
||||||
|
// manager.authChallenge();
|
||||||
|
// manager.authKSeFToken();
|
||||||
|
// manager.redeemToken();
|
||||||
|
// manager.openInteractiveSession();
|
||||||
|
|
||||||
// Create API Service and inject manager
|
// Create API Service and inject manager
|
||||||
apiService = new KSeFAPIService();
|
apiService = new KSeFAPIService();
|
||||||
apiService.kseFAuthManager = manager;
|
apiService.kseFAuthManager = manager;
|
||||||
|
|
@ -66,6 +74,10 @@ public class KSeFAPIServiceTest {
|
||||||
assertFalse(referenceNumber.isEmpty(), "Reference number should not be empty");
|
assertFalse(referenceNumber.isEmpty(), "Reference number should not be empty");
|
||||||
|
|
||||||
logger.info("✅ Upload successful - Reference Number: " + referenceNumber);
|
logger.info("✅ Upload successful - Reference Number: " + referenceNumber);
|
||||||
|
|
||||||
|
FileData file = workitem.getFileData("example-invoice-01.xml");
|
||||||
|
ItemCollection fileAttributes = new ItemCollection(file.getAttributes());
|
||||||
|
assertEquals(referenceNumber, fileAttributes.getItemValueString("ksef.referenceNumber"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemCollection createWorkitem() throws IOException {
|
private ItemCollection createWorkitem() throws IOException {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.alexanderlogistics.ksef;
|
||||||
|
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import pl.akmf.ksef.sdk.api.services.DefaultCryptographyService;
|
||||||
|
import pl.akmf.ksef.sdk.client.interfaces.KSeFClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dieser Test testet den offiziellen KSeF Client
|
||||||
|
*
|
||||||
|
* <dependency>
|
||||||
|
* <groupId>pl.akmf.ksef-sdk</groupId>
|
||||||
|
* <artifactId>ksef-client</artifactId>
|
||||||
|
* <version>3.0.4</version>
|
||||||
|
* <scope>test</scope>
|
||||||
|
* </dependency>
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
public class KSeFClientTest {
|
||||||
|
|
||||||
|
private static Logger logger = Logger.getLogger(KSeFClientTest.class.getName());
|
||||||
|
|
||||||
|
private DefaultCryptographyService cryptographyService;
|
||||||
|
private KSeFClient ksefClient;
|
||||||
|
private String accessToken;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() throws Exception {
|
||||||
|
// Simply instantiate as POJOs
|
||||||
|
// cryptographyService = new DefaultCryptographyService();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// 1) Challenge abholen
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// @Test
|
||||||
|
// @Order(1)
|
||||||
|
// @DisplayName("POST /api/v2/auth/token/redeem")
|
||||||
|
// void testRedeemToken() throws Exception {
|
||||||
|
|
||||||
|
// EncryptionData encryptionData = cryptographyService.getEncryptionData();
|
||||||
|
|
||||||
|
// OpenOnlineSessionRequest request = new OpenOnlineSessionRequestBuilder()
|
||||||
|
// .withFormCode(new FormCode(SystemCode.FA_3, SchemaVersion.VERSION_1_0E,
|
||||||
|
// SessionValue.FA))
|
||||||
|
// .withEncryptionInfo(encryptionData.encryptionInfo())
|
||||||
|
// .build();
|
||||||
|
|
||||||
|
// OpenOnlineSessionResponse openOnlineSessionResponse =
|
||||||
|
// ksefClient.openOnlineSession(request,
|
||||||
|
// accessToken);
|
||||||
|
// logger.info("OK");
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
</Adres>
|
</Adres>
|
||||||
<DaneKontaktowe>
|
<DaneKontaktowe>
|
||||||
<Email>Lucja17@example.net</Email>
|
<Email>Lucja17@example.net</Email>
|
||||||
<Telefon>86-588-03-553</Telefon>
|
<Telefon>86-588-03-5531</Telefon>
|
||||||
</DaneKontaktowe>
|
</DaneKontaktowe>
|
||||||
</Podmiot1>
|
</Podmiot1>
|
||||||
<Podmiot2>
|
<Podmiot2>
|
||||||
|
|
@ -92,7 +92,7 @@
|
||||||
<FaWiersz>
|
<FaWiersz>
|
||||||
<NrWierszaFa>3</NrWierszaFa>
|
<NrWierszaFa>3</NrWierszaFa>
|
||||||
<UU_ID>8647a6b5-f12a-3817-869e-010e4353686a</UU_ID>
|
<UU_ID>8647a6b5-f12a-3817-869e-010e4353686a</UU_ID>
|
||||||
<P_7>Incredible Steel Salad</P_7>
|
<P_7>Incredible Steel Salad blaasdff</P_7>
|
||||||
<P_8A>szt.</P_8A>
|
<P_8A>szt.</P_8A>
|
||||||
<P_8B>8</P_8B>
|
<P_8B>8</P_8B>
|
||||||
<P_9A>11.13</P_9A>
|
<P_9A>11.13</P_9A>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue