Przejdź do głównej zawartości

Architektura adaptera KSeF

Adapter KSeF opakowuje niskopoziomowego klienta API (KsefClient) w stabilny interfejs (KsefAdapter). Dzieki temu mozesz wymienic klienta bez zmiany kodu biznesowego.

┌─────────────┐ ┌──────────────────┐ ┌─────────────┐ ┌──────────┐
│ Twoj kod │────>│ KsefAdapterImpl │────>│ KsefClient │────>│ KSeF API │
│ (SDK/CLI) │ │ (adapter) │ │ (pluggable) │ │ (MF) │
└─────────────┘ └──────────────────┘ └─────────────┘ └──────────┘

Publiczny interfejs, z ktorym pracuje reszta aplikacji:

interface KsefAdapter {
fetchInvoices(opts: FetchInvoicesOpts): Promise<Invoice[]>
sendInvoice(input: SendInvoiceInput): Promise<SendInvoiceResult>
getUpo(ksefReference: string): Promise<UpoResult>
}

Niskopoziomowy klient — mozna go wymienic na symulator, mock lub wlasna implementacje:

interface KsefClient {
initSession(config: KsefClientConfig): Promise<KsefSessionState>
terminateSession(token: string): Promise<void>
fetchInvoices(params: {
token: string
dateFrom: string
dateTo: string
subjectNip?: string
pageSize?: number
pageOffset?: number
}): Promise<{ invoices: KsefRawInvoice[]; total: number }>
sendInvoice(params: {
token: string
xml: string
}): Promise<{ ksefReferenceNumber: string; timestamp: string }>
getUpo(params: {
token: string
ksefReferenceNumber: string
}): Promise<{ xml: string; status: 'confirmed' | 'pending' | 'rejected' }>
}

Glowna implementacja adaptera laczaca KsefClient z zarzadzaniem sesja:

class KsefAdapterImpl implements KsefAdapter {
private session: KsefSessionState | null = null
constructor(
private readonly client: KsefClient,
private readonly config: KsefClientConfig,
) {}
async initSession(): Promise<void> {
this.session = await this.client.initSession(this.config)
}
async closeSession(): Promise<void> {
if (this.session) {
await this.client.terminateSession(this.session.token)
this.session = null
}
}
private ensureSession(): KsefSessionState {
if (!this.session) {
throw new Error('KSeF session not initialized. Call initSession() first.')
}
return this.session
}
async fetchInvoices(opts: FetchInvoicesOpts): Promise<Invoice[]> {
const session = this.ensureSession()
const result = await this.client.fetchInvoices({
token: session.token,
dateFrom: opts.from,
dateTo: opts.to,
subjectNip: opts.nip,
pageSize: opts.pageSize,
pageOffset: opts.pageOffset,
})
return result.invoices.map((raw) => ({ /* ... mapowanie */ }))
}
async sendInvoice(input: SendInvoiceInput): Promise<SendInvoiceResult> {
const session = this.ensureSession()
// ... delegacja do client.sendInvoice()
}
async getUpo(ksefReference: string): Promise<UpoResult> {
const session = this.ensureSession()
// ... delegacja do client.getUpo()
}
}

initSession()

Tworzy nowa sesje KSeF. Musi byc wywolana przed jakimkolwiek zapytaniem.

closeSession()

Zamyka sesje i zwalnia zasoby. Bezpieczna do wywolania wielokrotnego.

ensureSession()

Prywatna metoda — rzuca blad jesli sesja nie zostala zainicjalizowana.

getSession()

Zwraca biezacy stan sesji lub null.

interface KsefSessionState {
token: string
nip: string
environment: 'production' | 'demo' | 'test'
expiresAt: Date
referenceNumber: string
}
ImplementacjaUzycie
Prawdziwy klientProdukcja — komunikacja z API KSeF Ministerstwa Finansow
@ksefnik/simulatorTesty — symulator offline z gotowymi scenariuszami
Wlasny adapterIntegracja z firmowym proxy lub API gateway
import { KsefAdapterImpl } from '@ksefnik/core'
import { createSimulatorClient } from '@ksefnik/simulator'
// Produkcja
const prodAdapter = new KsefAdapterImpl(realClient, prodConfig)
// Testy
const testAdapter = new KsefAdapterImpl(createSimulatorClient(), testConfig)

Adapter mozna wzbogacic o dodatkowe mechanizmy: