Stripe DATEV export – automated.You've built Stripe in. You don't have to code your accounting too.
finHero pulls your Stripe payments, fees, refunds and payouts and turns them into a ready-made DATEV booking batch including receipts. Your tax advisor imports it directly – no CSV wrangling, no home-grown export scripts.
The problem
Stripe data isn't accounting-ready
The Stripe CSV export looks like accounting – but it isn't. At the latest at year-end, your tax advisor will come back with questions.
“Paid” isn't money in the bank
Stripe pays out net and in bundles. Fees, refunds and disputes have to be booked separately, or the balances won't match.
Receipts are missing
Every booking needs a receipt. Downloading and matching invoices and credit notes one by one every month takes hours.
Subscriptions need accruals
If you sell annual plans, revenue must be spread over the right periods (deferred revenue). By hand, that's error-prone.
Building it yourself eats time
An export script is quick to write. DATEV format, charts of accounts, edge cases and maintenance cost you weeks that belong to your product.
The result
What your tax advisor gets
A finished DATEV-format export every month – traceable down to the individual Stripe transaction.
- ✓Booking batch in DATEV format, ready to import
- ✓Invoices and credit notes as PDF, linked to the bookings
- ✓Stripe fees and payouts booked cleanly and separately
- ✓Transaction IDs in the booking text for quick tracing
- ✓Accounts per SKR03 or SKR04, aligned with your tax advisor
| Amount € | Account | Contra account | Booking text |
|---|---|---|---|
| 49,00 | 1360 | 8400 | Stripe payment · Pro plan · ch_3Q…8xK |
| 1,99 | 4970 | 1360 | Stripe fee · ch_3Q…8xK |
| 490,00 | 1360 | 990 | Annual plan · deferral (pRAP) |
| 19,00 | 8400 | 1360 | Refund · re_3Q…2Lm |
| 2.384,40 | 1200 | 1360 | Stripe payout · po_1Q…7Vb |
How it works
Three steps to an automatic DATEV export
No code, no webhooks, no cron job.
Connect Stripe
Install the finHero app from the Stripe Marketplace. A few clicks, no programming.
Set up accounts
Choose SKR03 or SKR04 and add your own accounts if needed – ideally together with your tax advisor.
Get your export
Every month your DATEV export is ready. Download it, send it to your tax advisor, done.
Build it yourself?
Sure, you could build this yourself.
As a developer, it's tempting. Here's what's behind it:
Built yourself
- Generate the DATEV booking batch format correctly
- Handle fees, payouts, refunds and disputes properly
- Fetch receipts and match them to bookings
- Calculate accruals for annual plans
- Keep up with every change to the Stripe API or chart of accounts
With finHero
- Works from the first month
- Booking logic can be aligned with your tax advisor
- Retroactive exports possible
- Cancel monthly
- Your time stays on your product
For developers: adding Stripe Checkout to Next.js
If Stripe isn't live yet: three typical setups with realistic defaults (incl. productId/priceId & subscription). Once the first payments come in, finHero takes over the DATEV export – no further code needed.
1) Checkout session (subscription) – API route
// app/api/checkout/session/route.ts
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: "2023-10-16" });
export async function POST(req: Request) {
const { email, productId, priceId, customerId } = await req.json();
// Falls nur productId vorhanden → zu Price (recurring) auflösen
let resolvedPriceId = priceId;
if (!resolvedPriceId && productId) {
const prices = await stripe.prices.list({ product: productId, active: true, limit: 10 });
resolvedPriceId = prices.data.find(p => p.recurring)?.id || prices.data[0]?.id;
}
if (!resolvedPriceId) {
return Response.json({ error: "Kein Price gefunden." }, { status: 400 });
}
// Customer wiederverwenden/erstellen
const customer = customerId ? customerId : (await stripe.customers.create({ email })).id;
const session = await stripe.checkout.sessions.create({
mode: "subscription",
customer,
line_items: [{ price: resolvedPriceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
allow_promotion_codes: true,
metadata: { source: "pre-landing", plan_hint: productId || "n/a" },
});
return Response.json({ sessionId: session.id });
}2) Client – redirect to checkout
// app/(marketing)/subscribe/page.tsx
"use client";
import { loadStripe } from "@stripe/stripe-js";
import { useState } from "react";
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
export default function SubscribePage() {
const [loading, setLoading] = useState(false);
async function handleSubscribe() {
setLoading(true);
const res = await fetch("/api/checkout/session", {
method: "POST",
body: JSON.stringify({
email: "kunde@example.com",
// eines von beiden:
productId: "prod_ABC123", // → Server löst zu recurring price auf
// priceId: "price_123",
}),
});
const { sessionId } = await res.json();
const stripe = await stripePromise;
await stripe?.redirectToCheckout({ sessionId });
setLoading(false);
}
return (
<button className="btn btn-primary" onClick={handleSubscribe} disabled={loading}>
{loading ? "Weiterleiten…" : "Abo starten"}
</button>
);
}3) Webhook – handling events (basics)
// app/api/stripe/webhook/route.ts
import Stripe from "stripe";
export const config = { api: { bodyParser: false } }; // nur Pages Router; App Router liefert raw body
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: "2023-10-16" });
export async function POST(req: Request) {
const sig = req.headers.get("stripe-signature")!;
const raw = await req.text();
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(raw, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch (err: any) {
return new Response(`Webhook Error: ${err.message}`, { status: 400 });
}
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
// TODO: Onboarding, Feature-Freischaltung …
}
if (event.type === "invoice.paid") {
const invoice = event.data.object as Stripe.Invoice;
// TODO: Rechnungsdaten für finHero/DATEV vorbereiten
}
return new Response("ok");
}FAQ
Frequently asked questions about the Stripe DATEV export
Can I export Stripe directly to DATEV?
Stripe itself doesn't offer a DATEV export. The CSV export contains raw data that first has to be translated into booking entries, accounts and receipts. finHero does exactly that and delivers a DATEV booking batch ready to import.
Are Stripe fees and payouts booked correctly?
Yes. Fees are booked separately to the matching expense account, and payouts serve as the reconciliation point with your bank account. That way the balances match.
How do receipts get into DATEV?
finHero exports invoices, credit notes and cancellations as PDF and links them to the matching bookings – ready for your tax advisor.
Do I have to write any code?
No. The connection runs through the finHero app in the Stripe Marketplace. You don't write code or set up webhooks.
Does it work with Stripe Billing and subscriptions?
Yes. Subscription invoices are exported too, and annual plans can be deferred over the correct periods (pRAP).
Can I export past months?
Yes. Retroactive exports are possible – handy if your bookkeeping needs catching up.
My company isn't based in Germany – does it still work?
DATEV is the standard at German tax firms. If your advisor uses a different system, exports outside of DATEV are possible as well.
Do I need a productId or a priceId for Stripe Checkout?
For subscriptions you pass a priceId. If you only have the productId, your backend can resolve the matching recurring price – see the code example above.
More questions? Visit the help center
No more Stripe CSVs for your accounting.
Connect Stripe in a few minutes and look at your first DATEV export.
Try free for 7 days7 days free · cancel monthly




