Skip to content

Ruby SDK

Offizielles Ruby SDK für thelawin.dev.

Erfordert Ruby 3.0+

Installation

In deinem Gemfile:

ruby
gem 'thelawin', '~> 0.2'

Dann:

bash
bundle install

Oder direkt installieren:

bash
gem install thelawin

Schnellstart

ruby
require 'thelawin'

client = Thelawin::Client.new(api_key: 'env_sandbox_xxx')

result = client.invoice
  .number('2026-001')
  .date('2026-01-15')
  .seller(name: 'Acme GmbH', vat_id: 'DE123456789', city: 'Berlin', country: 'DE')
  .buyer(name: 'Kunde AG', city: 'München', country: 'DE')
  .add_item(description: 'Beratung', quantity: 8, unit: 'HUR', unit_price: 150, vat_rate: 19)
  .template('minimal')
  .generate

if result.success?
  result.save_pdf('./rechnung.pdf')
  puts "Erstellt: #{result.filename}"
  puts "Format: #{result.format.format_used}"
else
  result.errors.each { |e| puts "#{e[:path]}: #{e[:message]}" }
end

Client-Optionen

ruby
client = Thelawin::Client.new(
  api_key: 'env_sandbox_xxx',
  environment: :production,  # :production oder :preview
  timeout: 30                # optional, Sekunden
)

Umgebungen

UmgebungURLBeschreibung
:productionhttps://api.thelawin.devProduktions-API (Standard)
:previewhttps://api.preview.thelawin.dev:3080Preview/Staging-API
ruby
# Globale Konfiguration
Thelawin.configure do |config|
  config.api_key = 'env_sandbox_xxx'
  config.environment = :preview
end

# Oder pro Client
client = Thelawin::Client.new(api_key: 'env_sandbox_xxx', environment: :preview)

# Umgebung prüfen
client.preview?     # => true
client.production?  # => false

Unterstützte Formate

FormatBeschreibungAusgabe
autoAuto-Erkennung basierend auf Ländern (Standard)PDF oder XML
zugferdZUGFeRD 2.3 (Deutschland/EU)PDF/A-3 + CII XML
facturxFactur-X 1.0 (Frankreich)PDF/A-3 + CII XML
xrechnungXRechnung 3.0 (Deutsche B2G)PDF/A-3 + UBL XML
pdfEinfaches PDF ohne XMLPDF
ublUBL 2.1 Invoicenur XML
ciiUN/CEFACT CIInur XML
peppolPeppol BIS Billing 3.0nur XML
fatturapaFatturaPA 1.2.1 (Italien)nur XML

Builder API

Rechnungsdetails

ruby
client.invoice
  .number('2026-001')           # Erforderlich
  .date('2026-01-15')           # Erforderlich, String oder Date
  .due_date('2026-02-15')       # Optional
  .currency('EUR')              # Standard: 'EUR'
  .notes('Vielen Dank!')        # Optional

Format & Profil

ruby
.format('zugferd')              # 'auto', 'zugferd', 'facturx', 'xrechnung', 'pdf', 'ubl', 'cii', 'peppol', 'fatturapa'
.profile('en16931')             # 'minimum', 'basic_wl', 'basic', 'en16931', 'extended'

Parteien

ruby
.seller(
  name: 'Acme GmbH',           # Erforderlich
  vat_id: 'DE123456789',       # Erforderlich für ZUGFeRD
  street: 'Hauptstraße 1',
  city: 'Berlin',              # Erforderlich
  postal_code: '10115',
  country: 'DE',               # Erforderlich
  email: 'rechnung@acme.de',   # Erforderlich für XRechnung
  peppol_id: '0088:123...',    # Für Peppol
  codice_fiscale: '...',       # Für FatturaPA
)
.buyer(
  name: 'Kunde AG',            # Erforderlich
  vat_id: 'DE987654321',
  city: 'München',             # Erforderlich
  country: 'DE',               # Erforderlich
  codice_destinatario: '...',  # Für FatturaPA
  pec: 'cliente@pec.it'        # Für FatturaPA
)

Positionen

ruby
.add_item(
  description: 'Beratung',     # Erforderlich
  quantity: 8,                 # Erforderlich
  unit: 'HUR',                 # Erforderlich (C62=Stück, HUR=Stunde, DAY=Tag)
  unit_price: 150.00,          # Erforderlich
  vat_rate: 19.0,              # Erforderlich
  natura: 'N2.2'               # Für FatturaPA (MwSt-Befreiung)
)

Format-spezifische Felder

ruby
.leitweg_id('04011000-12345-67')   # XRechnung: B2G-Routing
.buyer_reference('PO-12345')        # Peppol: Bestellreferenz
.tipo_documento('TD01')             # FatturaPA: Dokumenttyp (TD01=Rechnung)

Anpassung

ruby
.template('minimal')           # 'minimal', 'classic', 'compact'
.locale('de')                  # 'en', 'de', 'fr', 'es', 'it'
.accent_color('#8b5cf6')
.footer_text('Vielen Dank!')
ruby
# Aus Datei (auto Base64)
.logo_file('./logo.png')
.logo_file('./logo.png', width_mm: 30)

# Aus Base64
.logo_base64('iVBORw0KGgo...', width_mm: 30)

Ergebnis-Handling

ruby
result = builder.generate

if result.success?
  puts result.filename           # 'rechnung-2026-001.pdf' oder '.xml'
  puts result.format.format_used # 'zugferd', 'fatturapa', etc.
  puts result.format.profile     # 'EN16931'
  puts result.format.version     # '2.3'

  # In Datei speichern
  result.save_pdf('./rechnung.pdf')  # oder result.save('./rechnung.xml')

  # Bytes abrufen
  pdf_bytes = result.to_bytes

  # Data URL abrufen
  data_url = result.to_data_url

  # Prüfen ob nur XML
  if result.xml_only?
    # XML-only Formate (UBL, Peppol, FatturaPA, etc.)
  end

  # Rechtliche Hinweise
  result.warnings.each do |warning|
    puts "#{warning.code}: #{warning.message}"
  end
else
  result.errors.each do |error|
    puts "#{error[:path]}: #{error[:message]}"
  end
end

Vor-Validierung (Dry-Run)

Rechnungsdaten validieren ohne PDF zu erzeugen:

ruby
result = client.invoice
  .number('2026-001')
  .date('2026-01-15')
  .seller(name: 'Acme', country: 'DE')
  .buyer(name: 'Kunde', country: 'IT')
  .add_item(description: 'Service', quantity: 1, unit_price: 100)
  .format('fatturapa')
  .validate  # Gibt DryRunResult zurück

if result.valid?
  puts "Gültig! Würde erzeugen: #{result.format.format_used}"
else
  result.errors.each { |e| puts e }
end

Fehlerbehandlung

ruby
begin
  result = client.invoice.generate
rescue Thelawin::QuotaExceededError
  puts 'Kontingent erschöpft, bitte Plan upgraden'
rescue Thelawin::NetworkError => e
  puts "Netzwerkfehler: #{e.message}"
rescue Thelawin::ApiError => e
  puts "API-Fehler #{e.status_code}: #{e.message}"
end

Format-spezifische Beispiele

XRechnung (Deutsche B2G)

ruby
result = client.invoice
  .format('xrechnung')
  .leitweg_id('04011000-12345-67')
  .seller(
    name: 'Acme GmbH',
    vat_id: 'DE123456789',
    email: 'rechnung@acme.de',  # Erforderlich
    street: 'Hauptstraße 1',
    city: 'Berlin',
    postal_code: '10115',
    country: 'DE'
  )
  # ... Rest der Rechnung
  .generate

Peppol

ruby
result = client.invoice
  .format('peppol')
  .buyer_reference('PO-12345')
  .seller(
    name: 'Acme Ltd',
    vat_id: 'GB123456789',
    peppol_id: '0088:1234567890123',
    city: 'London', country: 'GB'
  )
  .buyer(
    name: 'Kunde BV',
    peppol_id: '0106:NL123456789B01',
    city: 'Amsterdam', country: 'NL'
  )
  .generate

FatturaPA (Italien)

ruby
result = client.invoice
  .format('fatturapa')
  .tipo_documento('TD01')
  .seller(
    name: 'Acme S.r.l.',
    vat_id: 'IT12345678901',
    codice_fiscale: '12345678901',
    city: 'Milano', country: 'IT'
  )
  .buyer(
    name: 'Cliente S.p.A.',
    vat_id: 'IT98765432109',
    codice_destinatario: 'ABCDEFG',  # oder pec: 'cliente@pec.it'
    city: 'Roma', country: 'IT'
  )
  .add_item(description: 'Consulenza', quantity: 10, unit_price: 100, vat_rate: 22.0)
  .generate

# FatturaPA gibt nur XML zurück
result.save('./fattura.xml')

Rails-Integration

ruby
# config/initializers/thelawin.rb
Thelawin.configure do |config|
  config.api_key = Rails.application.credentials.thelawin_api_key
  config.environment = Rails.env.production? ? :production : :preview
end

# In deinem Controller/Service
class InvoiceService
  def generate_invoice(order)
    Thelawin.client.invoice
      .number(order.invoice_number)
      .date(order.created_at.to_date)
      .seller(company_details)
      .buyer(customer_party(order.customer))
      .items(order.line_items.map { |li| line_item_attrs(li) })
      .generate
  end
end

Quellcode

github.com/steviee/thelawin-clients/tree/main/ruby

Vollständige Dokumentation: Ruby SDK

ZUGFeRD 2.3 & Factur-X 1.0 compliant