For SaaS founders & developers

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.

✓ DATEV booking batch + receipts✓ SKR03 & SKR04✓ Cancel monthly
finHero · DATEV export
Buchungsstapel June 2026Today · synced automatically from Stripe
SK10
Stripe Payments
Revenue · SKR03 8400
12.480,00 €
Booked
SK11
Stripe Billing
Subscription revenue · monthly
3.899,00 €
Exported
GEB
Stripe fees
Expense · SKR03 4970
− 214,90 €
Receipt export
RAP
Deferred income
Annual subscriptions · pRAP
7.600,00 €
Accrual

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.

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
Buchungsstapel · DATEV format
Amount €AccountContra accountBooking text
49,0013608400Stripe payment · Pro plan · ch_3Q…8xK
1,9949701360Stripe fee · ch_3Q…8xK
490,001360990Annual plan · deferral (pRAP)
19,0084001360Refund · re_3Q…2Lm
2.384,4012001360Stripe payout · po_1Q…7Vb

Three steps to an automatic DATEV export

No code, no webhooks, no cron job.

1

Connect Stripe

Install the finHero app from the Stripe Marketplace. A few clicks, no programming.

2

Set up accounts

Choose SKR03 or SKR04 and add your own accounts if needed – ideally together with your tax advisor.

3

Get your export

Every month your DATEV export is ready. Download it, send it to your tax advisor, done.

View finHero on the Stripe App Marketplace

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

code
// 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

code
// 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)

code
// 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");
}

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 days

7 days free · cancel monthly