No description
  • Go 49.6%
  • TypeScript 34.8%
  • Shell 6.3%
  • NSIS 5.6%
  • HTML 2.9%
  • Other 0.8%
Find a file
Ksawery Wróbel 69ddfc68f2
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/tag/ci Pipeline was successful
fix ci on release
2026-05-01 20:51:57 +02:00
.woodpecker fix ci on release 2026-05-01 20:51:57 +02:00
build use ci image builder 2026-05-01 20:42:56 +02:00
cmd add stuff 2026-04-19 16:37:40 +02:00
frontend vibecode some other stuff 2026-04-26 18:40:15 +02:00
internal vibecode some other stuff 2026-04-26 18:40:15 +02:00
scripts add ci 2026-05-01 19:16:49 +02:00
services vibecode some other stuff 2026-04-26 18:40:15 +02:00
.gitignore add zod schemas 2026-04-20 06:43:55 +02:00
CLAUDE.md vibecode some other stuff 2026-04-26 18:40:15 +02:00
go.mod add stuff 2026-04-19 16:37:40 +02:00
go.sum add stuff 2026-04-19 16:37:40 +02:00
LICENSE add stuff 2026-04-19 16:37:40 +02:00
main.go add zod schemas 2026-04-20 06:43:55 +02:00
README.md add more cache 2026-05-01 20:05:41 +02:00
Taskfile.yml add stuff 2026-04-19 16:37:40 +02:00

faktury

A tiny desktop app for issuing Polish e-invoices: builds the FA(3) XML, submits it to KSeF, renders a PDF for the foreign buyer, stores artifacts on your shared drive, and emails a copy.

Built for a common Polish freelancer workflow: one or two recurring B2B clients, monthly consulting, USD/EUR reverse-charge — plus domestic PLN invoicing with real VAT once you add a Polish client.

Wails Go React Licence

Features

  • KSeF 2.0 end-to-end — long-lived token autoryzacyjny in Keychain, automatic access-token exchange + refresh, AES-256-CBC session encryption, FA(3) submission, UPO fetch.
  • PDF for the foreign buyer — rendered with headless Chrome (via chromedp), i18n labels (English / Polish / bilingual), KSeF QR code following the official qr.ksef.mf.gov.pl URL format.
  • NBP exchange rate — table A, previous business day, walks back through weekends/holidays automatically.
  • OpenCloud / any mounted share — artifacts (PDF + FA(3) XML + UPO XML) go to a folder tree on disk.
  • Gmail SMTP — app-password in Keychain, multipart email with the PDF attached, dry-run mode that emails only you.
  • Desktop app — Wails v3 + React 19 + Tailwind v4 + shadcn/ui. Single ~15 MB binary.

Status

Used in anger for a single foreign reverse-charge client. Submit-flow verified live against api-demo.ksef.mf.gov.pl. Polish domestic VAT breakdown (23% / 8% / 0% line items) is a known gap — see Limitations.

Requirements

  • Build: Go 1.26+, Node 24+, Wails v3 CLI (go install github.com/wailsapp/wails/v3/cmd/wails3@latest)
  • Runtime: macOS 11+ / Linux / Windows 11. A Chrome/Chromium install for PDF rendering (brew install --cask google-chrome on macOS).
  • KSeF account: a long-lived token autoryzacyjny generated in the MCU (Moduł Certyfikatów i Uprawnień). For the test environment self-signed certs are allowed; see MF's demo C# app. For demo / prod you need a qualified signature, qualified seal, or Profil Zaufany sign-flow via gov.pl.

Quick start

git clone https://code.forgejo.org/pyoxa/faktury.git
cd faktury

go install github.com/wailsapp/wails/v3/cmd/wails3@latest
wails3 build

./bin/faktury

On first launch you'll go through a 7-step wizard: seller details, default buyer, invoice template, drive path, Gmail app password, KSeF environment + token, and a review screen.

Profiles

Every setting and every Keychain secret is scoped to a profile. Switch between profiles from the titlebar menu (click profile: <name> ▾ next to the app title) — create a new one, switch, or delete with one-click.

  • Config files live at ~/Library/Application Support/faktury/profiles/<name>/config.json.
  • Keychain service is dev.pyoxa.faktury for the default profile and dev.pyoxa.faktury.<name> otherwise. Deleting a profile wipes both.
  • Launch with FAKTURY_PROFILE=demo ./bin/faktury to force-start in a specific profile. Without the env var, the app restores whichever profile was last active (persisted in ~/Library/Application Support/faktury/state.json).
  • Upgrading a pre-profiles install auto-migrates the existing config into profiles/default/ on first launch; nothing is lost.

Smoke-testing KSeF without the UI

export KSEF_TOKEN='<your long-lived token>'
go run ./cmd/ksef-probe -env demo -nip <your-NIP>           # auth only
go run ./cmd/ksef-probe -env demo -nip <your-NIP> -submit   # full submit
unset KSEF_TOKEN

Run it after every change to internal/fa3 — the XSD is strict and the error messages from MF's validator are the fastest debug channel.

Project layout

cmd/
  ksef-probe/      # live-API smoke test CLI
  icongen/         # app-icon generator
internal/
  config/          # JSON settings in ~/Library/Application Support/faktury
  keychain/        # macOS Keychain wrapper (zalando/go-keyring)
  invoice/         # Seller / Buyer / LineItem / Invoice domain types
  nbp/             # NBP table-A exchange-rate fetcher
  fa3/             # FA(3) XML builder + normaliser
  ksef/            # KSeF 2.0 HTTP client (auth + session + submit)
  pdf/             # chromedp-based HTML-to-PDF renderer + i18n labels
  qr/              # KSeF verification-URL + PNG generator
  storage/         # per-invoice directory tree writer
  mailer/          # Gmail SMTP multipart sender
services/          # Wails-exposed services consumed by the frontend
frontend/          # React 19 + Vite 8 + Tailwind 4 + shadcn/ui

Limitations / known gaps

  • Domestic Polish VAT is not yet covered. The FA(3) builder only emits the reverse-charge-to-foreign-buyer branch (P_13_8). Adding 23% / 8% / 0% PLN invoicing needs the full P_13_1..5 + P_14_1..5 block and a real gross/net distinction. Tracked as scope for v0.2.
  • Single-client presets. No multi-client switcher yet; the wizard edits a single buyer profile. Adding clients is on the roadmap.
  • Profil Zaufany flow for prod token generation is not built in — you download the unsigned AuthTokenRequest XML, sign it at gov.pl/web/gov/podpisz-dokument-elektronicznie-wykorzystaj-podpis-zaufany, upload back. That's a one-time ceremony; the token the app holds is long-lived.
  • Unsigned macOS builds. No Apple Developer ID yet, so the first launch needs a right-click → Open (Gatekeeper warning). Same for Windows.

Releases

Woodpecker runs CI on pushes to main, PRs targeting main, and v* tags. Tag pipelines build the Linux binary, AppImage, deb, and a Windows zip, then attach everything in dist/ to the Forgejo release.

Woodpecker's Forgejo OAuth integration handles repository access and provides metadata such as CI_FORGE_URL, but release publishing still needs an API token. Add a repository secret named forgejo_release_token with write:repository and read:misc scopes:

woodpecker-cli repo secret add \
  --repository pyoxa/faktury \
  --name forgejo_release_token \
  --value <forgejo-token> \
  --event tag \
  --image woodpeckerci/plugin-release

Cut a release from a clean, up-to-date main checkout:

scripts/tag-release.sh v0.1.0

macOS isn't cross-compilable — on your Mac, build + upload manually:

wails3 task darwin:dmg:universal VERSION=v0.1.0
# then drag bin/faktury-v0.1.0-universal.dmg into the Forgejo release page

Contributing

PRs welcome. Please keep changes focused and include a brief note in the commit message about whether the change was validated against the live api-demo.ksef.mf.gov.pl or is still in "compiles clean" territory.

Licence

MIT — see LICENSE.