Skip to content

Commit 7f49db5

Browse files
committed
release 3.0.8
1 parent e233931 commit 7f49db5

13 files changed

Lines changed: 210 additions & 32 deletions

File tree

RELEASE_NOTES.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,4 +1394,43 @@
13941394
|-------------|---------------|
13951395
| ➕ dodane | 0 |
13961396
| 🔧 zmienione | 3 |
1397+
| ➖ usunięte | 0 |
1398+
1399+
---
1400+
# Changelog zmian - `## 3.0.8 (2025-12-10)` - `API: 2.0.0 RC6.0`
1401+
1402+
## 1. ksef-client
1403+
1404+
### 1.1 api
1405+
- **DefaultKsefClient.java**: 🔧 Dodano parametr `UpoVersion upoVersion` w metodach `openBatchSession` i `openOnlineSession`, pozostałe oznaczone jako deprecated
1406+
1407+
### 1.2 client.interfaces
1408+
- **KSeFClient.java**: 🔧 metody `openBatchSession` i `openOnlineSession` zgodnie z opisem w implementacji `DefaultKsefClient`
1409+
- **CryptographyService.java**: 🔧 dodanie metody `PrivateKey parseEncryptedEcdsaPrivateKeyFromPem(byte[] pemBytes, char[] password)`
1410+
1411+
### 1.3 api.services
1412+
- **DefaultCryptographyService.java**: 🔧 dodanie metody `PrivateKey parseEncryptedEcdsaPrivateKeyFromPem(byte[] pemBytes, char[] password)`
1413+
- **DefaultVerificationLinkService.java**: 🔧 poprawka w generowaniu linku weryfikacyjnego
1414+
1415+
### 1.4 client.model
1416+
- **limit/GetRateLimitResponse.java**: 🔧 aktualizacja kontraktu - zmiana nazwy pola z `otherRateLimit` na `other`
1417+
- **UpoVersion.java**: ➕ enum z wartościami dla nagłówka `X-KSeF-Feature` dla zwracanego UPO
1418+
1419+
### 1.5 test - api.services
1420+
- **QrCodeTests.java**: ➕ poprawki testów
1421+
1422+
## 2. demo-web-app
1423+
1424+
### 2.1 integrationTest
1425+
- **QrCodeOfflineIntegrationTest.java**: 🔧 rozbudowa scenariusza testowego dla klucza prywatnego ECC wygenerowanego z aplikacji podatnika
1426+
1427+
### 2.2 integrationTest.resources
1428+
- **keys/private/ecdsa/sample/testowy_klucz_sdk.key**: ➕ dodanie przykładowego klucza prywatnego ECC wygenerowanego z aplikacji podatnika
1429+
1430+
## 3. Podsumowanie
1431+
1432+
| Typ zmiany | Liczba plików |
1433+
|-------------|---------------|
1434+
| ➕ dodane | 2 |
1435+
| 🔧 zmienione | 8 |
13971436
| ➖ usunięte | 0 |

demo-web-app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ plugins {
88
}
99

1010
group = "pl.akmf.ksef"
11-
version = "3.0.7"
11+
version = "3.0.8"
1212

1313
java {
1414
toolchain {

demo-web-app/src/integrationTest/java/pl/akmf/ksef/sdk/QrCodeOfflineIntegrationTest.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.testcontainers.shaded.org.bouncycastle.pkcs.PKCS10CertificationRequest;
1717
import org.testcontainers.shaded.org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
1818
import org.testcontainers.shaded.org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
19+
import pl.akmf.ksef.sdk.api.builders.certificate.CertificateMetadataListRequestBuilder;
1920
import pl.akmf.ksef.sdk.api.builders.certificate.SendCertificateEnrollmentRequestBuilder;
2021
import pl.akmf.ksef.sdk.api.builders.session.OpenOnlineSessionRequestBuilder;
2122
import pl.akmf.ksef.sdk.api.builders.session.SendInvoiceOnlineSessionRequestBuilder;
@@ -25,10 +26,14 @@
2526
import pl.akmf.ksef.sdk.client.model.certificate.CertificateEnrollmentResponse;
2627
import pl.akmf.ksef.sdk.client.model.certificate.CertificateEnrollmentStatusResponse;
2728
import pl.akmf.ksef.sdk.client.model.certificate.CertificateEnrollmentsInfoResponse;
29+
import pl.akmf.ksef.sdk.client.model.certificate.CertificateInfo;
30+
import pl.akmf.ksef.sdk.client.model.certificate.CertificateListItemStatus;
2831
import pl.akmf.ksef.sdk.client.model.certificate.CertificateListRequest;
2932
import pl.akmf.ksef.sdk.client.model.certificate.CertificateListResponse;
33+
import pl.akmf.ksef.sdk.client.model.certificate.CertificateMetadataListResponse;
3034
import pl.akmf.ksef.sdk.client.model.certificate.CertificateType;
3135
import pl.akmf.ksef.sdk.client.model.certificate.CsrResult;
36+
import pl.akmf.ksef.sdk.client.model.certificate.QueryCertificatesRequest;
3237
import pl.akmf.ksef.sdk.client.model.certificate.RetrieveCertificatesListItem;
3338
import pl.akmf.ksef.sdk.client.model.certificate.SendCertificateEnrollmentRequest;
3439
import pl.akmf.ksef.sdk.client.model.qrcode.ContextIdentifierType;
@@ -227,18 +232,31 @@ public void sendCsr() throws IOException, OperatorCreationException, NoSuchAlgor
227232

228233
static Stream<Arguments> inputQrCodeOfflineE2ETestReadKeyFromDiscParameters() {
229234
return Stream.of(
230-
Arguments.of(SystemCode.FA_2, "invoice-template.xml"),
231-
Arguments.of(SystemCode.FA_3, "invoice-template_v3.xml")
235+
// wersja z wygenerowanym wcześniej przez SDK kluczem RSA (+ wysłanym requestem enrolment z metody wyżej sendCsr() )
236+
Arguments.of("7368335898", "01732D26F736E531", EncryptionMethod.Rsa, SystemCode.FA_2, "invoice-template.xml", "/keys/private/rsa/sample/private-key.pem"),
237+
Arguments.of("7368335898", "01732D26F736E531", EncryptionMethod.Rsa, SystemCode.FA_3, "invoice-template_v3.xml", "/keys/private/rsa/sample/private-key.pem"),
238+
// wersja z wygenrowanym kluczem ECC przez aplikacje podatnika, enrolment również był wysłany przez aplikacje podatnika
239+
// https://web2te-ksef.mf.gov.pl/web/certificates/certificate-list - logujemy sie nipem - uwaga - z racji, że każdy może nawiązać sesję tym nipem, może być sytuacja, w której inna osoba unieważni certyfikat o podanym serial number w teście i testy przestaną działać, w assercji niżej jest to sprawdzane
240+
Arguments.of("8096529464", "01D1732E43B9345E", EncryptionMethod.ECDsa, SystemCode.FA_2, "invoice-template.xml", "/keys/private/ecdsa/sample/testowy_klucz_sdk.key"),// wazny 2 lata od 05.12.2025
241+
Arguments.of("8096529464", "01D1732E43B9345E", EncryptionMethod.ECDsa, SystemCode.FA_3, "invoice-template_v3.xml", "/keys/private/ecdsa/sample/testowy_klucz_sdk.key")// wazny 2 lata od 05.12.2025
232242
);
233243
}
234244

235245
@ParameterizedTest
236246
@MethodSource("inputQrCodeOfflineE2ETestReadKeyFromDiscParameters")
237-
public void qrCodeOfflineE2ETestReadKeyFromDisc(SystemCode systemCode, String invoiceTemplate) throws ApiException, JAXBException, IOException, InterruptedException {
238-
String privateKeyPath = "/keys/private/rsa/sample/private-key.pem";
239-
// wprowadzamy swój serial number i nip zgodny z tym który wysłaliśmy w QrCodeOfflineIntegrationTest.sendCsr
240-
String contextNip = "7368335898";
241-
String certificateSerialNumber = "015FA8CD52D35F23";
247+
public void qrCodeOfflineE2ETestReadKeyFromDisc(String contextNip, String certificateSerialNumber, EncryptionMethod encryptionMethod, SystemCode systemCode, String invoiceTemplate, String privateKeyPath) throws ApiException, JAXBException, IOException, InterruptedException {
248+
String accessToken = authWithCustomNip(contextNip, contextNip).accessToken();
249+
QueryCertificatesRequest request = new CertificateMetadataListRequestBuilder()
250+
.build();
251+
CertificateMetadataListResponse response = ksefClient.getCertificateMetadataList(request, 10, 0, accessToken);
252+
CertificateInfo certificateInfo = response.getCertificates()
253+
.stream()
254+
.filter(e -> e.getCertificateSerialNumber().equals(certificateSerialNumber))
255+
.filter(c -> CertificateType.OFFLINE.equals(c.getType()))
256+
.findFirst()
257+
.orElseThrow();
258+
Assertions.assertTrue(certificateInfo.getStatus().equals(CertificateListItemStatus.ACTIVE)); // jeśli ktos deaktywował to należy sobie wygenerować nowy i w parametrach metody/testu podać nowy nip i serial number
259+
// powyzsze testowe sprawdzenie czy certyfikat w resourcach dalej jest aktywny
242260

243261
//=====od tego momentu jestem całkowicie offline, nie mam dostępu do KSeF=====
244262
//Przygotowanie faktury w formacie XML
@@ -275,8 +293,11 @@ public void qrCodeOfflineE2ETestReadKeyFromDisc(SystemCode systemCode, String in
275293
contextNip,
276294
certificateSerialNumber,
277295
invoiceHash,
278-
defaultCryptographyService.parseRsaPrivateKeyFromPem(privateKey)
296+
EncryptionMethod.Rsa.equals(encryptionMethod)
297+
? defaultCryptographyService.parseRsaPrivateKeyFromPem(privateKey)
298+
: defaultCryptographyService.parseEncryptedEcdsaPrivateKeyFromPem(privateKey, "ADadADad12!@adadad".toCharArray()) // haslo przekazac w sposob bezpieczny, np z env variable, czy secret managerem
279299
);
300+
280301
checkIssuerMetadataByVerificationUrl(url);
281302
Arrays.fill(privateKey, (byte) 0);
282303

@@ -290,7 +311,7 @@ public void qrCodeOfflineE2ETestReadKeyFromDisc(SystemCode systemCode, String in
290311

291312
// zakładam, że jestem spowrotem online
292313
// nawiązujemy sesję, wysyłamy fakturę, weryfikujemy faktuę poprzez link weryfikacyjny
293-
String accessToken = authWithCustomNip(contextNip, contextNip).accessToken();
314+
accessToken = authWithCustomNip(contextNip, contextNip).accessToken();
294315
openSessionAdnSendInvoice(invoice, invoiceMetadata, systemCode, accessToken);
295316
checkInvoiceByVerificationUrl(invoiceForOfflineUrl, true);
296317
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-----BEGIN ENCRYPTED PRIVATE KEY-----
2+
MIHzMF4GCSqGSIb3DQEFDTBRMDAGCSqGSIb3DQEFDDAjBBBsO/Q+/Yau5LzBHLKW
3+
PKp7AgMBhqAwCgYIKoZIhvcNAgkwHQYJYIZIAWUDBAEqBBDdSuXSMik+Z4oC/DUI
4+
T9JNBIGQpKtIQ/hwAAt29F5U2p97x2RCsLpwvkjjc2640rIJdG9UW2bDmdHMAfY9
5+
Lhk55fTgWnuH1IKDrZ9hiQDH3g299dIo3p4bgH2l7itzaelq2xh628uvnY+rW41Q
6+
v1VnrF2uB/VSchOcUeAg9Ro2ORO2nbR3izRpnC6/bsS5cO4d+0OB9gISyqR7qJS0
7+
YG26aZP6
8+
-----END ENCRYPTED PRIVATE KEY-----

demo-web-app/src/test/java/pl/akmf/ksef/sdk/api/services/QrCodeTests.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import pl.akmf.ksef.sdk.client.model.certificate.SelfSignedCertificate;
88
import pl.akmf.ksef.sdk.client.model.qrcode.ContextIdentifierType;
99
import pl.akmf.ksef.sdk.system.SystemKSeFSDKException;
10-
import pl.akmf.ksef.sdk.util.ExampleApiProperties;
1110

1211
import java.nio.charset.StandardCharsets;
1312
import java.security.MessageDigest;
@@ -19,8 +18,8 @@
1918
import static org.junit.Assert.assertTrue;
2019

2120
class QrCodeTests {
22-
private ExampleApiProperties exampleApiProperties = new ExampleApiProperties();
2321

22+
private String apiUrl = "https://ksef-test.mf.gov.pl/";
2423

2524
// =============================================
2625
// Testy RSA; NIEZALECANE):
@@ -32,7 +31,7 @@ class QrCodeTests {
3231
// =============================================
3332
@Test
3433
void buildCertificateQr_WithEmbeddedPrivateKey_ShouldReturnBase64Png() throws Exception {
35-
VerificationLinkService linkSvc = new DefaultVerificationLinkService(exampleApiProperties);
34+
VerificationLinkService linkSvc = new DefaultVerificationLinkService(apiUrl);
3635
QrCodeService qrSvc = new DefaultQrCodeService();
3736
CertificateBuilders.X500NameHolder x500 = new CertificateBuilders()
3837
.buildForOrganization("Kowalski sp. z o.o", "VATPL-1111111111", "Kowalski", "PL");
@@ -56,7 +55,7 @@ void buildCertificateQr_WithEmbeddedPrivateKey_ShouldReturnBase64Png() throws Ex
5655

5756
@Test
5857
void buildCertificateQr_PublicOnlyWithoutPrivateKey_ShouldThrow() throws Exception {
59-
VerificationLinkService linkSvc = new DefaultVerificationLinkService(exampleApiProperties);
58+
VerificationLinkService linkSvc = new DefaultVerificationLinkService(apiUrl);
6059
String nip = "0000000000";
6160
String xml = "<x/>";
6261
String serial = UUID.randomUUID().toString();
@@ -76,7 +75,7 @@ void buildCertificateQr_PublicOnlyWithoutPrivateKey_ShouldThrow() throws Excepti
7675
// =============================================
7776
@Test
7877
void buildCertificateQr_WithEmbeddedEccPrivateKey_ShouldReturnBase64Png() throws Exception {
79-
VerificationLinkService linkSvc = new DefaultVerificationLinkService(exampleApiProperties);
78+
VerificationLinkService linkSvc = new DefaultVerificationLinkService(apiUrl);
8079
QrCodeService qrSvc = new DefaultQrCodeService();
8180
CertificateBuilders.X500NameHolder x500 = new CertificateBuilders()
8281
.buildForOrganization("Kowalski sp. z o.o", "VATPL-1111111111", "TestEcc", "PL");

ksef-client/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66

7-
val appVersion = "3.0.7"
7+
val appVersion = "3.0.8"
88
val artifactName = "ksef-client"
99

1010
val githubRepositoryToken = "token"

ksef-client/src/main/java/pl/akmf/ksef/sdk/api/DefaultKsefClient.java

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pl.akmf.ksef.sdk.client.model.ApiException;
1010
import pl.akmf.ksef.sdk.client.model.ApiResponse;
1111
import pl.akmf.ksef.sdk.client.model.ExceptionResponse;
12+
import pl.akmf.ksef.sdk.client.model.UpoVersion;
1213
import pl.akmf.ksef.sdk.client.model.auth.AuthKsefTokenRequest;
1314
import pl.akmf.ksef.sdk.client.model.auth.AuthOperationStatusResponse;
1415
import pl.akmf.ksef.sdk.client.model.auth.AuthStatus;
@@ -297,7 +298,7 @@ public OpenBatchSessionResponse openBatchSession(OpenBatchSessionRequest openBat
297298
headers.put(AUTHORIZATION, BEARER + accessToken);
298299
headers.put(CONTENT_TYPE, APPLICATION_JSON);
299300
headers.put(ACCEPT, APPLICATION_JSON);
300-
headers.put(X_KSEF_FEATURE, "upo-v4-3");
301+
headers.put(X_KSEF_FEATURE, UpoVersion.UPO_4_3.value());
301302

302303
HttpResponse<byte[]> response = post(BATCH_SESSION_OPEN.getUrl(), openBatchSessionRequest, headers);
303304

@@ -309,18 +310,32 @@ public OpenBatchSessionResponse openBatchSession(OpenBatchSessionRequest openBat
309310
* Otwiera sesję do wysyłki wsadowej faktur.
310311
*
311312
* @param openBatchSessionRequest - OpenBatchSessionRequest - schemat wysyłanych faktur, informacje o paczce faktur oraz informacje o kluczu używanym do szyfrowania.
312-
* @param upoVersion - Opcjonalna wersja formatu UPO. Dostępne wartości: "upo-v4-3". Generuje nagłówek X-KSeF-Feature z odpowiednią wartością. Domyślnie: v4-2 (v4-3 od 05.01.2026).
313+
* @param upoVersion - Opcjonalna wersja formatu UPO. Dostępne wartości: "upo-v4-3". Generuje nagłówek X-KSeF-Feature z odpowiednią wartością. Domyślnie: v4-2 (v4-3 od 05.01.2026).
313314
* @return OpenBatchSessionResponse
314315
* @throws ApiException - Nieprawidłowe żądanie. (400 Bad request)
315316
* @throws ApiException - Brak autoryzacji. (401 Unauthorized)
316317
*/
317318
@Override
319+
public OpenBatchSessionResponse openBatchSession(OpenBatchSessionRequest openBatchSessionRequest, UpoVersion upoVersion, String accessToken) throws ApiException {
320+
Map<String, String> headers = new HashMap<>();
321+
headers.put(AUTHORIZATION, BEARER + accessToken);
322+
headers.put(CONTENT_TYPE, APPLICATION_JSON);
323+
headers.put(ACCEPT, APPLICATION_JSON);
324+
headers.put(X_KSEF_FEATURE, upoVersion.value());
325+
326+
HttpResponse<byte[]> response = post(BATCH_SESSION_OPEN.getUrl(), openBatchSessionRequest, headers);
327+
328+
return getResponse(response, CREATED, BATCH_SESSION_OPEN, OpenBatchSessionResponse.class);
329+
}
330+
331+
@Override
332+
@Deprecated
318333
public OpenBatchSessionResponse openBatchSession(OpenBatchSessionRequest openBatchSessionRequest, String upoVersion, String accessToken) throws ApiException {
319334
Map<String, String> headers = new HashMap<>();
320335
headers.put(AUTHORIZATION, BEARER + accessToken);
321336
headers.put(CONTENT_TYPE, APPLICATION_JSON);
322337
headers.put(ACCEPT, APPLICATION_JSON);
323-
headers.put(X_KSEF_FEATURE, upoVersion);
338+
headers.put(X_KSEF_FEATURE, UpoVersion.fromValue(upoVersion).value());
324339

325340
HttpResponse<byte[]> response = post(BATCH_SESSION_OPEN.getUrl(), openBatchSessionRequest, headers);
326341

@@ -429,7 +444,7 @@ public OpenOnlineSessionResponse openOnlineSession(OpenOnlineSessionRequest open
429444
headers.put(AUTHORIZATION, BEARER + accessToken);
430445
headers.put(CONTENT_TYPE, APPLICATION_JSON);
431446
headers.put(ACCEPT, APPLICATION_JSON);
432-
headers.put(X_KSEF_FEATURE, "upo-v4-3");
447+
headers.put(X_KSEF_FEATURE, UpoVersion.UPO_4_3.value());
433448

434449
HttpResponse<byte[]> response = post(SESSION_OPEN.getUrl(), openOnlineSessionRequest, headers);
435450

@@ -441,17 +456,31 @@ public OpenOnlineSessionResponse openOnlineSession(OpenOnlineSessionRequest open
441456
* Inicjalizacja wysyłki interaktywnej faktur.
442457
*
443458
* @param openOnlineSessionRequest (optional)
444-
* @param upoVersion - Opcjonalna wersja formatu UPO. Dostępne wartości: "upo-v4-3". Generuje nagłówek X-KSeF-Feature z odpowiednią wartością. Domyślnie: v4-2 (v4-3 od 05.01.2026).
459+
* @param upoVersion - Opcjonalna wersja formatu UPO. Dostępne wartości: "upo-v4-3". Generuje nagłówek X-KSeF-Feature z odpowiednią wartością. Domyślnie: v4-2 (v4-3 od 05.01.2026).
445460
* @return ApiResponse&lt;OpenOnlineSessionResponse&gt;
446461
* @throws ApiException if fails to make API call
447462
*/
448463
@Override
464+
public OpenOnlineSessionResponse openOnlineSession(OpenOnlineSessionRequest openOnlineSessionRequest, UpoVersion upoVersion, String accessToken) throws ApiException {
465+
Map<String, String> headers = new HashMap<>();
466+
headers.put(AUTHORIZATION, BEARER + accessToken);
467+
headers.put(CONTENT_TYPE, APPLICATION_JSON);
468+
headers.put(ACCEPT, APPLICATION_JSON);
469+
headers.put(X_KSEF_FEATURE, upoVersion.value());
470+
471+
HttpResponse<byte[]> response = post(SESSION_OPEN.getUrl(), openOnlineSessionRequest, headers);
472+
473+
return getResponse(response, CREATED, SESSION_OPEN, OpenOnlineSessionResponse.class);
474+
}
475+
476+
@Override
477+
@Deprecated
449478
public OpenOnlineSessionResponse openOnlineSession(OpenOnlineSessionRequest openOnlineSessionRequest, String upoVersion, String accessToken) throws ApiException {
450479
Map<String, String> headers = new HashMap<>();
451480
headers.put(AUTHORIZATION, BEARER + accessToken);
452481
headers.put(CONTENT_TYPE, APPLICATION_JSON);
453482
headers.put(ACCEPT, APPLICATION_JSON);
454-
headers.put(X_KSEF_FEATURE, upoVersion);
483+
headers.put(X_KSEF_FEATURE, UpoVersion.fromValue(upoVersion).value());
455484

456485
HttpResponse<byte[]> response = post(SESSION_OPEN.getUrl(), openOnlineSessionRequest, headers);
457486

0 commit comments

Comments
 (0)