DATEV Integration with Stripe: A Technical Guide for German Companies
Technical guide to integrating Stripe payment data with DATEV: webhook sync, Buchungsstapel export, tax mapping, GoBD compliance, and reconciliation automation.
Summary
German companies using Stripe need automated DATEV integration for GoBD-compliant bookkeeping. This guide covers webhook-based sync, Buchungsstapel CSV generation, tax account mapping, and daily reconciliation between Stripe balance transactions and DATEV entries.
Definition
DATEV integration with Stripe connects Stripe payment events to DATEV Unternehmen online for automated bookkeeping. It maps charges, refunds, fees, and payouts to DATEV Konten (accounts), generates Buchungsstapel (batch booking entries), and maintains GoBD-compliant audit trails.
Why Manual Export Fails
Finance teams that export Stripe CSV monthly and manually create DATEV entries waste 8–15 hours per month and introduce errors. Common failures:
- Stripe fees booked separately from gross revenue (SKR mapping errors)
- Refunds not matched to original charge booking
- Payout timing mismatch (Stripe payout date ≠ bank deposit date)
- Missing USt (VAT) split on mixed-tax invoices
- No audit trail linking DATEV entry to Stripe object ID
Automated integration eliminates these and satisfies GoBD Anwendungserlass requirements for electronic bookkeeping.
Architecture
Stripe Webhooks → Event Processor → Booking Rules Engine → DATEV Export
↓ ↓ ↓ ↓
charge.succeeded Normalize SKR03 mapping Buchungsstapel CSV
payout.paid Deduplicate Tax calculation DATEVconnect API
charge.refunded Validate Fee allocation Unternehmen online
Step 1: Kontenrahmen Mapping
Define Stripe-to-DATEV account mapping for SKR03 (most common for GmbH):
| Stripe Event | Soll (Debit) | Haben (Credit) | Amount |
|---|---|---|---|
| charge.succeeded (net) | 1200 (Bank) | 8400 (Erlöse) | Net amount |
| charge.succeeded (USt 19%) | - | 1776 (USt 19%) | Tax amount |
| Stripe fee | 4970 (Nebenkosten) | 1200 (Bank) | Fee amount |
| charge.refunded | 8400 (Erlöse) | 1200 (Bank) | Refund amount |
| payout.paid | 1200 (Bank) | 1360 (Stripe Geldtransit) | Payout amount |
| dispute lost | 4970 (Nebenkosten) | 1200 (Bank) | Dispute amount |
Store mapping in configuration, not hardcoded:
const SKR03_MAPPING = {
charge: {
debit: '1200', // Bank (Stripe Transit)
credit: '8400', // Erlöse 19%
taxCredit: '1776' // USt 19%
},
fee: {
debit: '4970', // Nebenkosten Geldverkehr
credit: '1200'
},
refund: {
debit: '8400',
credit: '1200'
},
payout: {
debit: '1200', // Bank (actual bank account)
credit: '1360' // Stripe Geldtransit
}
};
Adjust for SKR04 or custom Kontenrahmen with your Steuerberater.
Step 2: Webhook Event Processing
Process Stripe events into normalized booking entries:
async function processStripeEvent(event) {
const handlers = {
'charge.succeeded': handleChargeSucceeded,
'charge.refunded': handleChargeRefunded,
'charge.dispute.closed': handleDisputeClosed,
'payout.paid': handlePayoutPaid,
'balance_transaction.created': handleBalanceTransaction
};
const handler = handlers[event.type];
if (!handler) return;
const existing = await db.bookings.findUnique({ where: { stripeEventId: event.id } });
if (existing) return; // Idempotent
const bookings = await handler(event.data.object);
await db.bookings.createMany({
data: bookings.map(b => ({
...b,
stripeEventId: event.id,
stripeObjectId: event.data.object.id,
status: 'pending_export'
}))
});
}
async function handleChargeSucceeded(charge) {
const netAmount = charge.amount - (charge.application_fee_amount || 0);
const taxAmount = charge.metadata?.tax_amount ? parseInt(charge.metadata.tax_amount) : 0;
const revenueAmount = netAmount - taxAmount;
const bookings = [
{
date: new Date(charge.created * 1000),
debitAccount: SKR03_MAPPING.charge.debit,
creditAccount: SKR03_MAPPING.charge.credit,
amount: revenueAmount,
currency: charge.currency.toUpperCase(),
reference: charge.id,
description: `Stripe Charge ${charge.id}`
}
];
if (taxAmount > 0) {
bookings.push({
date: new Date(charge.created * 1000),
debitAccount: SKR03_MAPPING.charge.debit,
creditAccount: SKR03_MAPPING.charge.taxCredit,
amount: taxAmount,
currency: charge.currency.toUpperCase(),
reference: charge.id,
description: `USt Stripe ${charge.id}`
});
}
// Stripe fee (from balance transaction)
const balanceTxn = await stripe.balanceTransactions.retrieve(charge.balance_transaction);
if (balanceTxn.fee > 0) {
bookings.push({
date: new Date(charge.created * 1000),
debitAccount: SKR03_MAPPING.fee.debit,
creditAccount: SKR03_MAPPING.fee.credit,
amount: balanceTxn.fee,
currency: charge.currency.toUpperCase(),
reference: charge.id,
description: `Stripe Fee ${charge.id}`
});
}
return bookings;
}
Step 3: Buchungsstapel Generation
Generate DATEV EXTF format (Buchungsstapel):
function generateBuchungsstapel(bookings, config) {
const header = [
'EXTF', '510', '21', 'Buchungsstapel', '7',
formatDate(new Date()), '', '', '', '', '',
config.beraterNr, config.mandantenNr,
formatDate(config.wirtschaftsjahrBeginn), '4',
formatDate(bookings[0].date), formatDate(bookings[bookings.length - 1].date),
'', 'EUR', '', '', '', '', '', '', '03', '', '', '', ''
].join(';');
const rows = bookings.map(b => [
b.amount.toFixed(2).replace('.', ','), // Umsatz
'H', // Soll/Haben (H=Haben on credit side)
'EUR', // Währung
'', // Kurs
'', // Basisumsatz
'', // WKZ
b.amount.toFixed(2).replace('.', ','), // Umsatz
b.creditAccount, // Gegenkonto
'', // BU-Schlüssel
formatDateDE(b.date), // Belegdatum
b.reference.slice(0, 12), // Belegnummer
b.description.slice(0, 60), // Belegtext
'1', // Postensperre
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''
].join(';'));
return [header, ...rows].join('\n');
}
function formatDateDE(date) {
return `${String(date.getDate()).padStart(2, '0')}${String(date.getMonth() + 1).padStart(2, '0')}${date.getFullYear()}`;
}
Step 4: DATEVconnect API Upload
For automated import (requires DATEVconnect license):
async function uploadToDATEV(buchungsstapel, config) {
const response = await fetch('https://accounting-d.datev.de/api/v1/clients/bookings', {
method: 'POST',
headers: {
'Authorization': `Bearer ${config.datevAccessToken}`,
'Content-Type': 'application/json',
'X-DATEV-Client-Id': config.datevClientId
},
body: JSON.stringify({
accounting_month: formatDateDE(new Date()).slice(2), // MMYYYY
entries: parseBuchungsstapelToJSON(buchungsstapel)
})
});
if (!response.ok) {
const error = await response.json();
throw new DATEVExportError(error.message, buchungsstapel);
}
return response.json();
}
Alternative: Export CSV to shared folder for DATEV Unternehmen online manual import. Less automated but works without DATEVconnect.
Step 5: Payout Reconciliation
Match Stripe payouts to bank deposits:
async function reconcilePayouts() {
const payouts = await stripe.payouts.list({ status: 'paid', limit: 100 });
const bankTransactions = await fetchBankTransactions(last30Days);
for (const payout of payouts.data) {
const bankMatch = bankTransactions.find(t =>
Math.abs(t.amount - payout.amount) < 100 && // Within €1
Math.abs(t.date - new Date(payout.arrival_date * 1000)) < 3 * 86400000 // Within 3 days
);
if (bankMatch) {
await db.reconciliation.update({
where: { stripePayoutId: payout.id },
data: { bankTransactionId: bankMatch.id, status: 'matched', matchedAt: new Date() }
});
} else {
await alertFinance({ type: 'unmatched_payout', payoutId: payout.id, amount: payout.amount });
}
}
}
Step 6: GoBD Compliance
Store complete audit trail:
// Every booking entry must be traceable
const auditEntry = {
stripeEventId: event.id,
stripeObjectId: charge.id,
stripeObjectType: 'charge',
bookingEntries: bookings,
datevExportId: exportResult?.id,
exportedAt: new Date(),
hash: sha256(JSON.stringify(bookings)), // Immutability proof
createdAt: new Date()
};
await db.auditLog.create({ data: auditEntry });
GoBD requirements met:
- Vollständigkeit: Every Stripe transaction produces a booking entry
- Richtigkeit: Amounts match Stripe balance transactions exactly
- Zeitgerechtheit: Daily export, not month-end batch
- Unveränderbarkeit: Hash-verified audit log, append-only
- Nachvollziehbarkeit: Stripe object ID in every DATEV Belegtext
Tax Handling
For Stripe Tax or manual tax calculation:
function calculateGermanTax(grossAmount, taxRate = 0.19) {
const net = Math.round(grossAmount / (1 + taxRate));
const tax = grossAmount - net;
return { net, tax, gross: grossAmount };
}
// Reverse charge B2B (USt-IdNr. verified)
function isReverseCharge(customer) {
return customer.tax_ids?.data?.some(t =>
t.type === 'eu_vat' && t.verification.status === 'verified'
);
}
Reverse charge bookings use BU-Schlüssel 94 (IG Erwerb) or 40 (§13b UStG) depending on service type. Confirm with Steuerberater.
Common Mistakes
- Booking gross amounts without USt split
- Recording Stripe fees as separate manual entries (should be automatic per charge)
- Using payout date instead of charge date for revenue recognition
- Missing refund-to-original-booking linkage
- No Stripe object ID in Belegtext (breaks audit trail)
- Monthly batch export instead of daily sync
Business Outcomes
Automated DATEV-Stripe integration saves 10–15 hours/month of manual bookkeeping, eliminates reconciliation errors at month-end, satisfies GoBD requirements, and gives Steuerberater clean Buchungsstapel files daily.
Related: E-Rechnung 2025 Guide · E-Rechnung integration
Need help connecting your finance systems?
Fynteq connects E-Rechnung, DATEV, Stripe, ERP and bank workflows for German SMEs and growing digital businesses. Frankfurt-based, fixed-scope implementation.
Frequently Asked Questions
Related articles
How to Automate Payment Reconciliation
Learn how to automate payment reconciliation: matching provider settlements to internal records, exception handling, and finance-ready reporting for SaaS and FinTech.
How to Automate Accounts Payable with n8n and AI (German Mittelstand Guide)
Step-by-step guide to AP automation for German Mittelstand using n8n workflows, AI invoice extraction, E-Rechnung processing, and DATEV integration.
E-Rechnung 2025: What Every German B2B Company Must Do Now
Practical guide to E-Rechnung compliance in Germany: XRechnung, ZUGFeRD, Peppol, DATEV integration, and what B2B companies must implement before the mandate expands.
Finance integration insights
Practical guides on E-Rechnung, DATEV, Stripe, reconciliation and finance automation for teams in Germany.
By downloading, you agree to our privacy policy. We use your email to send the PDF and follow up on related services. Or contact info@fynteq.com.