Ksef - Portfolio

Menu
Przejdź do treści
Klasa KSeFClient umożliwia komunikację z API Krajowego Systemu e-Faktur (KSeF). Klasa zarządza nawiązywaniem sesji, uzyskaniem tokenu sesji, wysyłaniem faktury oraz zamykaniem sesji. Wykorzystuje bibliotekę cURL do wysyłania żądań HTTP.
Właściwości klasy:
  • $apiUrl: Adres URL API KSeF, z którym klasa się komunikuje.
  • $nip: Numer identyfikacyjny podatnika (NIP), wymagany do autoryzacji.
  • $apiKey: Klucz API, służący do autoryzacji.
  • $publicKeyPath: Ścieżka do klucza publicznego (plik PEM) używanego do szyfrowania tokenu sesji.
  • $sessionToken: Token sesji otrzymany z KSeF, wymagany do wykonywania operacji w ramach sesji.
Metody klasy:
  1. __construct($apiUrl, $nip, $apiKey, $publicKeyPath): Konstruktor klasy inicjalizujący właściwości na podstawie przekazanych parametrów.
  2. sendRequest($url, $data, $headers, $method = 'POST'): Metoda pomocnicza, która wysyła żądania HTTP do API KSeF. Obsługuje różne metody HTTP (np. POST, GET), a także sprawdza status odpowiedzi oraz ewentualne błędy cURL.
  3. getChallengeAndTimestamp(): Wysyła żądanie o wyzwanie (challenge) i znacznik czasu (timestamp) wymagane do autoryzacji. Odpowiedź zawiera dane w formacie JSON, które są niezbędne do utworzenia tokenu sesji.
  4. encryptToken($token, $challengeTimeMillis): Szyfruje token sesji przy użyciu klucza publicznego. Łączy token z wyzwaniem (challenge) w formie token|challengeTimeMillis, a następnie szyfruje i koduje wynik w Base64.
  5. getKSeFSessionToken($encryptedToken, $challenge): Wysyła zaszyfrowany token sesji w formacie XML, aby uzyskać rzeczywisty token sesji z KSeF. Wykorzystuje DOMDocument do tworzenia struktury XML wymaganej przez API.
  6. sendInvoice($invoiceFile): Wysyła fakturę do systemu KSeF. Odczytuje plik faktury, generuje SHA-256 jako hash faktury i wysyła fakturę zakodowaną w Base64. W przypadku sukcesu zwraca odpowiedź z systemu.
  7. terminateSession(): Zamyka aktywną sesję KSeF. Wysyła żądanie zamknięcia sesji z użyciem sessionToken.

Opis funkcji:
  1. getChallengeAndTimestamp: Używana do otrzymania wyzwania (challenge) i znacznika czasu, aby przygotować szyfrowanie tokenu sesji.
  2. encryptToken: Wykorzystuje szyfrowanie asymetryczne z OpenSSL, aby zabezpieczyć token sesji. Funkcja ta jest kluczowa dla poprawnej autoryzacji w KSeF.
  3. sendInvoice: Odpowiada za dostarczenie dokumentu faktury do KSeF.

<?php
class KSeFClient {
   private $apiUrl;
   private $nip;
   private $apiKey;
   private $publicKeyPath;
   private $sessionToken;
   public function __construct($apiUrl, $nip, $apiKey, $publicKeyPath) {
       $this->apiUrl = $apiUrl;
       $this->nip = $nip;
       $this->apiKey = $apiKey;
       $this->publicKeyPath = $publicKeyPath;
   }
   private function sendRequest($url, $data, $headers, $method = 'POST') {
       $curl = curl_init();
       curl_setopt($curl, CURLOPT_URL, $url);
       curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
       curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
       curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
       if ($data) {
           curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
       }
       $response = curl_exec($curl);
       $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
       $curlError = curl_error($curl);
       curl_close($curl);
       if ($curlError) {
           echo "Błąd cURL: " . $curlError . "\n";
       }
       return ['response' => $response, 'httpCode' => $httpCode];
   }
   public function getChallengeAndTimestamp() {
       $url = "{$this->apiUrl}/online/Session/AuthorisationChallenge";
       $data = json_encode([
           "contextIdentifier" => [
               "type" => "onip",
               "identifier" => $this->nip
           ]
       ]);
       $headers = ["Content-Type: application/json", "Accept: application/json"];
       $response = $this->sendRequest($url, $data, $headers);
       if ($response['httpCode'] === 201) {
           return json_decode($response['response'], true);
       } else {
           die("Błąd w uzyskiwaniu challenge: " . $response['response']);
       }
   }
   private function encryptToken($token, $challengeTimeMillis) {
       $dataToEncrypt = "$token|$challengeTimeMillis";
       $encryptedToken = '';
       $publicKey = file_get_contents($this->publicKeyPath);
       if (openssl_public_encrypt($dataToEncrypt, $encryptedToken, $publicKey, OPENSSL_PKCS1_PADDING)) {
           return base64_encode($encryptedToken);
       } else {
           echo "Nie udało się zaszyfrować tokenu.\n";
           return false;
       }
   }
   public function getKSeFSessionToken($encryptedToken, $challenge) {
       $dom = new DOMDocument('1.0', 'UTF-8');
       $dom->formatOutput = true;
       $root = $dom->createElement('ns3:InitSessionTokenRequest');
       $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns2', 'http://ksef.mf.gov.pl/schema/gtw/svc/types/2021/10/01/0001');
       $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns3', 'http://ksef.mf.gov.pl/schema/gtw/svc/online/auth/request/2021/10/01/0001');
       $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns4', 'http://ksef.mf.gov.pl/schema/gtw/svc/online/types/2021/10/01/0001');
       $dom->appendChild($root);
       $context = $dom->createElement('ns3:Context');
       $root->appendChild($context);
       $context->appendChild($dom->createElement('ns4:Challenge', $challenge));
       $identifier = $dom->createElement('ns4:Identifier');
       $identifier->setAttribute('xsi:type', 'ns2:SubjectIdentifierByCompanyType');
       $identifier->appendChild($dom->createElement('ns2:Identifier', $this->nip));
       $context->appendChild($identifier);
       $tokenElement = $dom->createElement('ns4:Token', trim($encryptedToken));
       $context->appendChild($tokenElement);
       $url = "{$this->apiUrl}/online/Session/InitToken";
       $headers = ["Content-Type: application/octet-stream", "Accept: application/json"];
       $response = $this->sendRequest($url, $dom->saveXML(), $headers);
       if ($response['httpCode'] === 200 || $response['httpCode'] === 201) {
           $this->sessionToken = json_decode($response['response'], true)['sessionToken']['token'];
           return $this->sessionToken;
       } else {
           echo "Błąd w uzyskiwaniu tokenu sesji.\n";
           return false;
       }
   }
   public function sendInvoice($invoiceFile) {
       if (!file_exists($invoiceFile) || !is_readable($invoiceFile)) {
           die("Plik faktury nie istnieje lub nie można go odczytać: $invoiceFile\n");
       }
       $invoiceData = file_get_contents($invoiceFile);
       $hashSHA = base64_encode(hash('sha256', $invoiceData, true));
       $invoiceBody = base64_encode($invoiceData);
       $fSize = filesize($invoiceFile);
       $body = json_encode([
           "invoiceHash" => [
               "fileSize" => $fSize,
               "hashSHA" => ["algorithm" => "SHA-256", "encoding" => "Base64", "value" => $hashSHA]
           ],
           "invoicePayload" => ["type" => "plain", "invoiceBody" => $invoiceBody]
       ]);
       $headers = [
           'Accept: application/json',
           'SessionToken: ' . $this->sessionToken,
           'Content-Type: application/json'
       ];
       $response = $this->sendRequest("{$this->apiUrl}/online/Invoice/Send", $body, $headers, 'PUT');
       $httpCode = $response['httpCode'];
       if ($httpCode === 200 || $httpCode === 201) {
           return json_decode($response['response'], true);
       } elseif ($httpCode === 202) {
           echo "Żądanie zaakceptowane do przetwarzania.\n";
           return json_decode($response['response'], true);
       } else {
           echo "Błąd w wysyłaniu faktury.\n";
           return false;
       }
   }
   public function terminateSession() {
       $url = "{$this->apiUrl}/online/Session/Terminate";
       $headers = ['Accept: application/json', 'SessionToken: ' . $this->sessionToken];
       $response = $this->sendRequest($url, null, $headers, 'GET');
       if ($response['httpCode'] === 200) {
           echo "Sesja została zakończona pomyślnie.\n";
           return json_decode($response['response'], true);
       } else {
           echo "Błąd zamknięcia sesji.\n";
           return false;
       }
   }
}
// Użycie klasy
$ksefClient = new KSeFClient("https://ksef-demo.mf.gov.pl/api", "nip", "ApiKey", __DIR__ . '/publicKey.pem');
$challengeData = $ksefClient->getChallengeAndTimestamp();
if ($challengeData) {
   $challenge = $challengeData['challenge'];
   $challengeTimeMillis = strtotime($challengeData['timestamp']) * 1000;
   $encryptedToken = $ksefClient->encryptToken("xxx", $challengeTimeMillis);
   if ($encryptedToken && $ksefClient->getKSeFSessionToken($encryptedToken, $challenge)) {
       $ksefClient->sendInvoice(__DIR__ . '/faktura.xml');
       $ksefClient->terminateSession();
   }
}
?>
Wróć do spisu treści