@jungherz-de/stripe-jack
v1.1.2
Published
Export Stripe transactions to DATEV (EXTF Buchungsstapel, BEDI documents, PRAP) — German accounting, cron-ready.
Maintainers
Readme
German bookkeeping expects a very specific package: an EXTF booking batch, the matching invoice PDFs in a BEDI archive, and a deferred-revenue schedule for subscriptions. StripeJack produces exactly that from a Stripe account, in one command, ready to upload to DATEV Unternehmen online.
It is verified against a real DATEV export produced by other software for the same month: in compatibility mode the output matches field for field, header timestamp aside.
Installation
npm install -g @jungherz-de/stripe-jackOr without installing:
npx @jungherz-de/stripe-jack export --month 2026-07Requires Node.js 20 or newer.
Quick Start
stripe-jack init # asks for your Stripe key and account numbers, writes .env
stripe-jack accounts # check the configuration, no API call
stripe-jack export # export the last completed monthinit defaults every account number from the chart of accounts you pick (SKR 03 or 04) and validates the result before writing, so a typo surfaces immediately rather than on the first export. Prefer to fill the file in yourself? Copy .env.example.
The archive lands in ./out, next to a manifest with counts, totals and checksums.
Configuration
stripe-jack init writes the file for you. It ends up looking like this — the full reference is in docs/configuration.md (German):
STRIPE_API_SEC_KEY=sk_live_…
# From the letter of your tax firm
DATEV_BERATER=1234567
DATEV_MANDANT=12345
DATEV_SACHKONTENLAENGE=4
EXTF_SKR=04
# Core accounts (SKR 04 shown; SKR 03 equivalents in the reference)
KONTO_DEBITOR=10004 # receivables
KONTO_GEBUEHREN=6855 # Stripe and PSP fees
KONTO_GELDTRANSIT=1461 # money in transit to the bank account
KONTO_GUTHABEN=1810 # the Stripe balance itself
KONTO_KLAERUNG=1370 # suspense account
ERLOES_REGEL=4400 # standard-rate revenueStripeJack only ever reads, so a restricted key (rk_live_…) belongs here rather than a full secret key. The quickest route is Stripe’s permission template for read-only accounting access; only Basic Business Contact Information has to be added as Read on top — that is how the merchant’s country is read, and the country decides every revenue account. No write access is needed at all. Full list: docs/configuration.md.
CLI
stripe-jack init # interactive setup, writes .env
stripe-jack export # last completed month, all accounts
stripe-jack export --month 2026-07
stripe-jack export --from 2026-07-01 --to 2026-07-31
stripe-jack export --account shop --out ./exports --no-mail
stripe-jack export --month 2026-07 --dry-run # report only, write nothing
stripe-jack export --month 2026-07 --force # replace an existing export
stripe-jack accounts # show the resolved configuration
stripe-jack verify --month 2026-07 --against old-export.zipExit codes: 0 success · 1 configuration · 2 Stripe/network · 3 data · 4 mail · 5 lock held. A failed mail never costs you the archive — it is on disk before the first byte goes out.
Running on a schedule
15 4 3 * * cd /srv/stripe-jack && /usr/bin/npx stripe-jack export >> /var/log/stripe-jack.log 2>&1With no arguments StripeJack exports the last completed month. Runs are locked per account, already-exported periods are not silently overwritten, and logs are JSON lines when stdout is not a terminal. systemd and launchd units are in docs/cron.md (German).
Multiple accounts
STRIPE_ACCOUNTS=main,shop
MAIN_STRIPE_API_SEC_KEY=sk_live_…
MAIN_DATEV_MANDANT=54321
SHOP_STRIPE_API_SEC_KEY=sk_live_…
SHOP_DATEV_MANDANT=40921Each account is exported into its own subdirectory with its own chart of accounts, lock and recipient. See docs/multi-account.md (German).
Sending the export
MAIL_ENABLED=true
MAIL_SMTP_HOST=smtp.example.com
[email protected]
[email protected]The mail carries the archive plus a summary of counts, totals and any warnings. --dry-run reports what would be sent without sending it.
What gets exported
DATEV_Stripe_<client>_<uuid>_2026-07.zip
├── Stripe_PRAP_…xlsx deferred revenue per subscription invoice
├── Income_By_Product_…xlsx net revenue per product, weekly/monthly/yearly
├── EXTF_Stripe_Zahlungen_…csv one batch of payment bookings
├── EXTF_Stripe_Rechnungen_…csv one batch of invoice bookings
└── documents.zip invoice PDFs + BEDI document.xml indexField-by-field description of the format: docs/datev-format.md (German).
Booking rules
The Stripe balance is modelled as a bank-like account. Payments clear receivables, fees become an expense, payouts move through a money-in-transit account, and refunds reverse the same account pair rather than swapping accounts — so tax automation on the revenue accounts keeps working.
The revenue account is derived, not configured per customer: domestic standard and reduced rate, intra-community reverse charge, One-Stop-Shop, third country. Anything StripeJack cannot classify with confidence goes to the suspense account with a warning, never to a plausible guess.
Full decision tree, the deferral maths and the foreign-currency handling: docs/booking-rules.md (German).
Replacing an existing tool
StripeJack ships two profiles:
| Profile | Behaviour |
|---|---|
| strict (default) | correct German bookkeeping |
| compat | follows the conventions of older export tools |
# Compare against an export produced by your current tool
stripe-jack verify --month 2026-07 --profile compat --against old-export.zip
# → No differences.Use compat for the switch-over month so nothing changes for your tax advisor, then move to strict. The five differences are documented in docs/compatibility.md (German).
Privacy and self-hosting
StripeJack runs on your machine. There is no service behind it, no account, no sign-up, and no server of ours that ever sees anything.
That matters for GDPR. An export carries personal data — customer names, addresses, invoice amounts and the invoice PDFs. With StripeJack the parties to that data are your company, Stripe (which already holds it) and your tax firm. No additional provider is introduced, so there is no processor to contract under Art. 28 GDPR, no third-party server location to assess, and no extra entry in your record of processing activities.
The tool opens exactly two kinds of outbound connection, both from your own machine: to api.stripe.com to read, and to your own SMTP server if — and only if — you switch mailing on. There is no telemetry, no usage reporting and no update check. The source is open, and every outbound call lives in src/stripe/ and src/output/mailer.ts.
Nothing is stored beyond the archive and its manifest in your output directory: no database, no cache, no temporary spool.
Details, including operating it under its own system user and what to do about access and erasure requests: docs/datenschutz.md (German).
Using it with Claude Code
The package ships a SKILL.md. Point Claude Code (or any agent that reads skill files) at this repository and it knows how to run the tool, what the exit codes mean, and how to walk you through setup by asking for the values it cannot guess.
"Set up StripeJack for my Stripe account and export last month."Nothing about the tool requires an agent — stripe-jack init does the same guided setup on its own.
Development
npm install
npm run typecheck
npm run lint
npm test # unit tests + an offline golden-master test
npm run buildThe golden-master test replays a recorded Stripe response and compares the result with a reference export for the same month. Both sides are anonymised by scripts/anonymize-fixture.ts, which applies one replacement table to the Stripe data and to the reference CSVs — pseudonyms keep the original string length, so the 60-character truncation the format relies on is still exercised.
No customer data and no live keys are in this repository.
License
MIT © Jungherz GmbH
StripeJack is not affiliated with Stripe, Inc. or DATEV eG. It produces import files according to publicly documented formats; verifying the resulting bookings remains your and your tax advisor's responsibility.
