Mehrwährungs-Rechnungsstellung klingt nach einem gelösten Problem: Währung wählen, mit einem Wechselkurs multiplizieren, die Summe ausgeben. In der Praxis ist sie einer der fehleranfälligsten Bereiche jedes Abrechnungssystems, und die Fehler sind teuer, weil sie in Ihren Forderungen, Ihren Steuererklärungen und den Postfächern Ihrer Kunden auftauchen. Wenn Sie als Entwickler Mehrwährungs-Rechnungsstellung in ein SaaS-Produkt, ein Freelancer-Tool, eine Agenturplattform oder einen B2B-Marktplatz integrieren, ist der schwierige Teil nicht die Multiplikation. Es geht darum, zu entscheiden, welchen Wechselkurs Sie verwenden, wann Sie ihn fixieren und wie Sie ihn speichern, damit eine im März ausgestellte Rechnung auch dann noch korrekt abgestimmt werden kann, wenn sie im Juli bezahlt wird.
Dieser Leitfaden geht die Engineering-Entscheidungen durch, auf die es ankommt, mit lauffähigem Code, der eine Wechselkurs-API nutzt, um Kurse abzurufen, zu fixieren und zu speichern. Der Fokus liegt gezielt auf der Devisenschicht (FX) — dem Teil, den die meisten Rechnungs-Tutorials überspringen.
Warum Mehrwährungs-Rechnungsstellung mehr ist als Währungsumrechnung
Eine Einwährungs-Rechnung ist eine Momentaufnahme: Menge mal Preis, plus Steuer. Eine Mehrwährungs-Rechnung ist ein Vertrag über einen Zeitpunkt. Wenn Sie einem Kunden in EUR Rechnung stellen, während Ihre Bücher in USD geführt werden, erfassen Sie eine Forderung, deren USD-Wert an dem Tag fixiert wird, an dem Sie sie ausstellen — auch wenn sich der Marktkurs weiterbewegt, bis der Kunde tatsächlich zahlt.
Diese Lücke erzeugt drei konkrete Probleme, die eine einfache Umrechnung ignoriert:
- Welcher Kurs gilt. Der Kurs am Rechnungsdatum, am Zahlungsdatum oder von „heute"? Sie sind fast nie identisch, und die Rechnungslegungsstandards (sowohl US-GAAP als auch IFRS) sind bei der Antwort eindeutig.
- Prüfbarkeit. Sie müssen Monate später genau nachweisen können, welchen Kurs Sie verwendet haben und woher er stammt. „Wir haben ihn von einer API geholt" reicht nicht, wenn Sie die Zahl nicht reproduzieren können.
- Devisengewinn und -verlust. Die Differenz zwischen dem Wert am Rechnungsdatum und dem Wert am Zahlungsdatum ist ein echter Gewinn oder Verlust, der irgendwo in Ihrem Hauptbuch landen muss.
Machen Sie das richtig, wird Mehrwährungs-Rechnungsstellung im besten Sinne langweilig. Machen Sie es falsch, verbringt Ihr Finanzteam die letzte Woche jedes Quartals damit, Cents hinterherzujagen.
Die goldene Regel: Fixieren Sie den Wechselkurs am Rechnungsdatum
Die wichtigste Regel der Mehrwährungs-Rechnungsstellung lautet: Frieren Sie den Wechselkurs im Moment der Rechnungsstellung ein und berechnen Sie ihn niemals neu.
Sowohl US-GAAP als auch IFRS verlangen, eine Fremdwährungstransaktion zum Kassakurs zu erfassen, der am Transaktionsdatum gilt. Bei einer Rechnung ist das Transaktionsdatum das Ausstellungsdatum. Wenn Sie einem Kunden am 1. Juli Rechnung stellen, Ihr System sie aber erst am 5. Juli verarbeitet, verwenden Sie trotzdem den Kurs vom 1. Juli. Das ist keine Finexly-Regel und keine Vorliebe — so wird der Wert der Forderung in Ihrer funktionalen (Heimat-)Währung rechtlich festgelegt.
Ein häufiges Anti-Pattern ist die Anzeige einer „live" umgerechneten Summe, die sich bei jeder Aktualisierung der Rechnung durch den Kunden ändert. Tun Sie das niemals. Eine Rechnung ist eine feste Forderung über einen bestimmten Betrag. Der Kunde schuldet den Betrag in der Rechnungswährung, und Ihre Bücher schulden eine Buchung zum fixierten Kurs. Der Markt kann danach tun, was er will.
Das praktische Fazit: Währungsumrechnung für die Rechnungsstellung ist eine Einmal-Schreiboperation. Sie rufen den Kurs einmal ab, speichern ihn mit der Rechnung und behandeln ihn für die gesamte Lebensdauer dieses Dokuments als unveränderlich.
Eine Wechselkurs-API für die Rechnungsstellung auswählen
Nicht jede FX-Datenquelle eignet sich für die Rechnungsstellung. Für die Abrechnung wollen Sie:
- Abdeckung jeder Währung, in der Sie abrechnen (Finexly deckt über 170 Währungen ab).
- Einen verlässlichen „Stand"-Zeitstempel auf jedem Kurs, damit Sie nachweisen können, welche Notierung Sie verwendet haben.
- Historische Kurse nach Datum, denn rückdatierte und korrigierte Rechnungen sind unvermeidlich.
- Vorhersehbare Ratenlimits und Preise, damit ein Anstieg des Rechnungsvolumens die Abrechnung nicht lahmlegt. Prüfen Sie die Preispläne, bevor Sie eine Abhängigkeit in Ihren Bezahlpfad einbauen.
Eine einfache Anfrage nach dem aktuellen Kurs sieht so aus:
curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR&access_key=YOUR_API_KEY"Und die Antwort liefert Ihnen einen Kurs plus den Zeitstempel, den Sie daneben speichern:
{
"success": true,
"base": "USD",
"timestamp": 1753660800,
"rates": {
"EUR": 0.9213
}
}Wenn Sie die Kurse interaktiv erkunden möchten, bevor Sie etwas verdrahten, ist der Währungsrechner eine schnelle Kontrolle. Für einen breiteren Blick darauf, wie sich Finexly mit anderen Anbietern vergleicht, siehe Währungs-APIs vergleichen.
Den Rechnungskurs abrufen und fixieren
Hier ist ein kleiner Python-Helfer, der den Kurs für eine Rechnung abruft und alles zurückgibt, was Sie persistieren müssen: den Kurs, den Quell-Zeitstempel und die umgerechnete Summe. Beachten Sie, dass er den Kurs und Metadaten zurückgibt — nicht nur eine Zahl.
import os
import time
import requests
from decimal import Decimal, ROUND_HALF_UP
API_KEY = os.environ["FINEXLY_API_KEY"]
BASE_URL = "https://api.finexly.com/v1/latest"
def lock_invoice_rate(home_currency, invoice_currency):
"""Fetch and lock the FX rate to convert an invoice total
(in invoice_currency) back into home_currency for the books."""
resp = requests.get(BASE_URL, params={
"base": invoice_currency,
"symbols": home_currency,
"access_key": API_KEY,
}, timeout=10)
resp.raise_for_status()
data = resp.json()
if not data.get("success"):
raise RuntimeError("Rate lookup failed")
rate = Decimal(str(data["rates"][home_currency]))
return {
"rate": rate, # invoice_currency -> home_currency
"rate_base": invoice_currency,
"rate_quote": home_currency,
"source": "finexly",
"as_of": data["timestamp"], # store the source timestamp
"locked_at": int(time.time()), # when WE locked it
}
def home_value(amount_invoice_ccy, rate):
"""Convert an invoice-currency amount into home currency."""
return (Decimal(str(amount_invoice_ccy)) * rate).quantize(
Decimal("0.01"), rounding=ROUND_HALF_UP
)Das entscheidende Detail ist, dass lock_invoice_rate einmalig bei der Rechnungserstellung läuft und seine Ausgabe in den Rechnungsdatensatz geschrieben wird. Für Produktionsthemen wie Caching, Wiederholungsversuche und das saubere Behandeln von 429-Antworten folgen Sie den Mustern in unserem Leitfaden zu Caching und Fehlerbehandlung — Sie wollen nicht, dass ein vorübergehender API-Aussetzer die Rechnungsausstellung blockiert.
Den Wechselkurs bei der Rechnung speichern
Da der Kurs pro Rechnung unveränderlich ist, speichern Sie ihn auf der Rechnung, nicht in einer gemeinsam genutzten Tabelle „aktueller Kurs", die Sie überschreiben könnten. Ein minimales Schema sieht so aus:
CREATE TABLE invoices (
id BIGSERIAL PRIMARY KEY,
issue_date DATE NOT NULL,
invoice_ccy CHAR(3) NOT NULL, -- what the customer is billed in
home_ccy CHAR(3) NOT NULL, -- your functional currency
total_invoice NUMERIC(18,2) NOT NULL, -- total in invoice_ccy
fx_rate NUMERIC(18,8) NOT NULL, -- invoice_ccy -> home_ccy, LOCKED
fx_source TEXT NOT NULL, -- e.g. 'finexly'
fx_as_of TIMESTAMPTZ NOT NULL, -- the rate's source timestamp
total_home NUMERIC(18,2) NOT NULL -- total_invoice * fx_rate, at issue
);Drei Dinge machen dieses Schema prüfungsfreundlich. Erstens verwendet fx_rate acht Dezimalstellen — Wechselkurse brauchen weit mehr Präzision als die zwei Dezimalstellen eines Geldbetrags, und ein zu frühes Abschneiden führt zu Rundungsdrift. Zweitens hält fx_as_of fest, woher die Zahl stammt und wann, sodass jeder Prüfer sie anhand historischer Daten reproduzieren kann. Drittens wird total_home zum Ausstellungszeitpunkt berechnet und gespeichert, sodass ein sechs Monate später erstellter Bericht nie raten muss.
Zur Transparenz gegenüber dem Kunden drucken Sie den fixierten Kurs auf die Rechnung selbst: den fälligen Betrag, die Währung, den verwendeten Kurs und das Datum, an dem er galt. Das ist eine weithin empfohlene bewährte Praxis, gerade weil sie die Mehrdeutigkeit beseitigt, wenn die Zahlung zu einem anderen Kurs eintrifft.
Rückdatierte Rechnungen: historische Kurse verwenden, nicht die von heute
Früher oder später stellen Sie eine Rechnung mit einem Datum in der Vergangenheit aus — eine Korrektur, eine verspätete Buchung oder ein Vertrag mit einem früheren Wirkungsdatum. Den heutigen Kurs für eine März-Rechnung zu verwenden, ist schlicht falsch und wird eine Prüfung nicht bestehen. Rufen Sie stattdessen den Kurs für das tatsächliche Rechnungsdatum von einem historischen Endpunkt ab:
def lock_historical_rate(home_currency, invoice_currency, invoice_date):
"""invoice_date as 'YYYY-MM-DD'. Returns the locked rate for a
backdated or corrected invoice."""
resp = requests.get("https://api.finexly.com/v1/historical", params={
"date": invoice_date,
"base": invoice_currency,
"symbols": home_currency,
"access_key": API_KEY,
}, timeout=10)
resp.raise_for_status()
data = resp.json()
rate = Decimal(str(data["rates"][home_currency]))
return {"rate": rate, "as_of": invoice_date, "source": "finexly"}Die Regel ist identisch mit dem Live-Fall — einmal fixieren, für immer speichern — aber das Datum, das Sie abfragen, ist das Ausstellungsdatum der Rechnung, nicht der aktuelle Tag. Für eine tiefere Behandlung der Arbeit mit datierten Kursen siehe den Leitfaden zur API für historische Wechselkurse.
Rundung und Präzision, richtig gemacht
Geld-Bugs sind fast immer Rundungs-Bugs. Zwei Regeln halten Sie aus dem Schlamassel heraus.
Verwenden Sie niemals Gleitkommazahlen für Geld. 0.1 + 0.2 ist in der Gleitkomma-Arithmetik nicht 0.3, und diese winzigen Fehler summieren sich über die Rechnungspositionen. Verwenden Sie einen Dezimaltyp: Decimal in Python, BigDecimal in Java, decimal in C# oder eine Ganzzahl-Cent-Darstellung in JavaScript.
Entscheiden Sie, wo gerundet wird. Runden Sie den Kurs auf volle Präzision (8+ Dezimalstellen), aber runden Sie Beträge auf die kleinste Einheit der Währung — zwei Stellen für USD oder EUR, null Stellen für JPY oder KRW, drei für einige andere. Ein häufiger Fehler ist, zwei Dezimalstellen fest zu codieren und eine Rechnung über ¥1,234.56 auszustellen, was kein gültiger Yen-Betrag ist. Leiten Sie die Anzahl der Dezimalstellen aus der Währung ab, unter Verwendung der ISO-4217-Daten zur kleinsten Einheit.
Bei Rechnungen mit Positionen sollten Sie jede Position runden und dann summieren und gegen die gerundete Summe abstimmen, damit die gedruckten Positionen die gedruckte Summe ergeben. Welche Konvention Sie auch wählen, wenden Sie sie konsistent über Rechnungsstellung, Zahlungen und Berichte an.
Devisengewinn und -verlust zum Zahlungszeitpunkt erfassen
Hier zahlt sich der fixierte Kurs aus. Wenn der Kunde zahlt, rechnen Sie das, was tatsächlich auf Ihrem Bankkonto eingegangen ist, zum Kurs des Zahlungsdatums in Ihre Heimatwährung zurück. Die Differenz zwischen diesem und dem Heimatwert des Rechnungsdatums ist ein realisierter Devisengewinn oder -verlust.
// Amounts kept as Decimal-like strings; use a money library in production.
function realizedFxGainLoss(invoice, paymentRate) {
// invoice.totalInvoice: amount billed, in the invoice currency
// invoice.fxRate: LOCKED rate at issue (invoice_ccy -> home_ccy)
// paymentRate: rate on the day the payment settled
const homeAtIssue = invoice.totalInvoice * invoice.fxRate;
const homeAtPayment = invoice.totalInvoice * paymentRate;
const gainLoss = homeAtPayment - homeAtIssue;
return {
homeAtIssue: round2(homeAtIssue),
homeAtPayment: round2(homeAtPayment),
fxGainLoss: round2(gainLoss), // > 0 gain, < 0 loss
};
}
function round2(n) { return Math.round(n * 100) / 100; }Sie rufen paymentRate genauso ab, wie Sie den Rechnungskurs fixiert haben — ein latest-Aufruf am Abwicklungstag oder ein historical-Aufruf, wenn Sie nachträglich abstimmen. Buchen Sie fxGainLoss auf ein dediziertes „Devisengewinn/-verlust"-Konto. Diese eine Buchung hält Ihre Bücher im Gleichgewicht, wenn sich der Markt zwischen Ausstellung und Zahlung bewegt, und genau diese Abstimmungsarbeit macht manuelle Mehrwährungs-Abrechnung falsch. Unternehmen, die dies in automatisierte Pipelines einbauen, können sich auf dieselbe Kursinfrastruktur stützen, die in unserem Leitfaden zur Mehrwährungs-SaaS-Abrechnung beschrieben ist.
Gutschriften, Rückerstattungen und wiederkehrende Rechnungen
Drei Grenzfälle runden ein vollständiges System ab:
- Gutschriften und Rückerstattungen müssen die ursprüngliche Rechnung zu ihrem ursprünglich fixierten Kurs stornieren — nicht zum aktuellen Kurs. Eine Rückerstattung ist die Auflösung der ursprünglichen Transaktion, verwenden Sie also den
fx_rateder Rechnung, die Sie gutschreiben, erneut. Jede Restdifferenz am tatsächlichen Rückerstattungsdatum wird zu einer weiteren kleinen Devisengewinn/-verlust-Buchung. - Wiederkehrende Rechnungen erhalten jeweils ihren eigenen fixierten Kurs an ihrem eigenen Ausstellungsdatum. Ein monatlich abgerechnetes 12-Monats-Abonnement erzeugt zwölf Rechnungen mit zwölf Kursen. Fixieren Sie keinen einzigen Kurs für das Jahr, es sei denn, der Vertrag legt ihn ausdrücklich fest.
- Vertraglich fixierte Kurse überschreiben gelegentlich den Markt — manche Unternehmensverträge legen einen festen Kurs für einen Zeitraum fest. Unterstützen Sie ein manuelles Überschreibungsfeld, speichern Sie es aber mit denselben Prüf-Metadaten, damit es reproduzierbar bleibt.
Häufig gestellte Fragen
Welchen Wechselkurs sollte ich auf einer Rechnung verwenden? Verwenden Sie den Kassakurs, der am Ausstellungsdatum der Rechnung gilt, und fixieren Sie ihn dann. Sowohl US-GAAP als auch IFRS verlangen, Fremdwährungstransaktionen zum Kurs des Transaktionsdatums zu erfassen, und das Rechnungsdatum ist dieses Datum. Berechnen Sie ihn nicht neu, wenn der Kunde die Rechnung ansieht oder bezahlt.
Sollte ich den Wechselkurs oder nur den umgerechneten Betrag speichern? Speichern Sie beides — plus Quelle und Zeitstempel des Kurses. Nur die umgerechnete Summe zu speichern, macht es unmöglich, später zu prüfen oder den Devisengewinn/-verlust korrekt zu berechnen. Kurs, „Stand"-Zeitstempel und Quelle zusammen erlauben es jedem, die Zahl zu reproduzieren.
Wie behandle ich eine Rechnung mit Datum in der Vergangenheit? Fragen Sie einen historischen Wechselkurs für das tatsächliche Ausstellungsdatum ab, statt den heutigen zu verwenden. Die Regel einmal fixieren, für immer speichern ist dieselbe; nur das Datum, das Sie nachschlagen, ändert sich.
Was verursacht Devisengewinn oder -verlust auf einer Rechnung? Die Marktbewegung zwischen Rechnungsdatum und Zahlungsdatum. Ihre Bücher haben die Forderung zum Kurs des Rechnungsdatums erfasst, aber das Geld trifft zum Kurs des Zahlungsdatums bewertet ein. Die Differenz ist ein realisierter Devisengewinn oder -verlust und wird auf ein dediziertes Hauptbuchkonto gebucht.
Brauche ich eine kostenpflichtige Währungs-API, um Mehrwährungs-Rechnungsstellung zu bauen? Sie können mit einem kostenlosen Tarif beginnen. Finexlys kostenlose Wechselkurs-API deckt Live- und historische Kurse für Volumen in der Frühphase ab, und Sie steigen erst auf, wenn Ihr Rechnungsdurchsatz wächst.
Erste Schritte
Mehrwährungs-Rechnungsstellung läuft auf eine Disziplin hinaus: Fixieren Sie den Kurs am Rechnungsdatum, speichern Sie ihn mit vollständigen Prüf-Metadaten und stimmen Sie die Differenz bei der Zahlung ab. Tun Sie das, und die internationale Abrechnung hört auf, ein Feuerwehreinsatz zum Quartalsende zu sein.
Bereit, es zu bauen? Holen Sie sich Ihren kostenlosen Finexly-API-Schlüssel — keine Kreditkarte erforderlich. Starten Sie mit Live- und historischen Kursen für über 170 Währungen im kostenlosen Tarif und skalieren Sie, während Ihr Rechnungsvolumen wächst.
Explore More
Vlado Grigirov
Senior Currency Markets Analyst & Financial Strategist
Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...
View full profile →