Scout Reimbursement Pro

Description

Scout Reimbursement Pro is a complete expense reimbursement system built specifically for scout groups, youth organizations, and non-profit associations. It solves a common problem in volunteer work: members advance money out of pocket — for camp supplies, craft materials, or travel costs — and then have to submit receipts manually via email. This plugin makes the entire process digital, transparent, and secure.

How it works

  1. A member fills out the Contact Form 7 form on your website and uploads receipts (photos or PDFs) directly from their phone.
  2. The board receives an email with all details and a one-click approval link.
  3. After approval, the treasurer automatically receives a structured payment email with IBAN suffix, amount, and receipt files.
  4. Once the treasurer has processed the payment, they mark it as “Paid” via a link — the applicant is notified automatically.
  5. The WordPress admin can monitor all submissions, status changes, and annual summaries in the backend at any time.

Key Features

Mobile-optimised file upload
The upload area is optimised for smartphone use. Members can take a photo of a receipt with their camera or upload an existing file (JPG, PNG, PDF). Multiple receipts per submission are supported.

Client-side image compression
Before uploading, photos are automatically compressed in the browser to a maximum of 1600×1600 pixels at 80% quality. This saves server capacity and speeds up uploads significantly — especially important with poor mobile connections at camps.

Secure file storage
Receipts are stored in a dedicated directory inside the uploads folder that is closed off with .htaccess and web.config, and they are never linked directly. Every receipt is delivered by a PHP endpoint that first checks permission: administrators get access through their login, while board members and the treasurer follow signed links from their notification e-mails that carry an expiry date. Uploaded files also get a random name component, so a receipt cannot be guessed even on servers that ignore .htaccess.

If your site runs on Nginx, .htaccess has no effect and the protection is incomplete: a receipt link contains the file path, so anyone holding a legitimate link can derive the plain uploads URL and keep using it after the signed link has expired. The plugin checks whether the directory is still reachable and, if it is, shows a warning on every plugin page together with the location block you need to add. Please do add it — on Nginx the endpoint alone is not enough.

Email-based approval workflow
The board approves with a single click from the e-mail — no WordPress login needed. Each stage has its own single-use token: the approval link cannot confirm a payment, and the payment link is only created once approval has happened. Only a hash of each token is stored, the token itself exists solely in the e-mail, and it stops working as soon as it has been used or its 60 days are up. If a link expires or the mail gets lost, the backend can send a fresh one.

Clicking the link opens a short confirmation page that submits the action itself, so the click still feels like one step. Because the change now happens over POST from that page, ordinary link scanners in mail gateways, link previews and browser prefetching no longer trigger an approval — they fetch the link but never run the page.

A scanner that fully renders pages including JavaScript could still complete the step. If your mail is filtered by a sandbox of that kind, switch the setting to two clicks: the link then shows the booking and waits for the button. One click stays the default because it is what most groups want from a volunteer board.

Three-stage status system
Each submission passes through the stages “Open” “Approved” “Paid”. Backward status changes are blocked — an already approved submission cannot be reversed via the email links. Administrators can reject any non-paid submission directly from the WordPress backend, even after approval (e.g. for duplicate receipts), with an optional reason sent to the applicant.

Automatic receipt numbers
Every approved submission automatically receives a sequential receipt number (N-1, N-2, …). A unique index in the database prevents the same number from being handed out twice, and numbers are never reused — a submission that is rejected after approval keeps its number, so the sequence stays auditable.

Audit report as a single PDF
Choose any period — freely selectable start and end date — and the plugin builds one complete PDF: a cover sheet with totals and signature lines for the auditors, an index of all receipts with page numbers, and a data sheet per receipt carrying the digital stamp (PAID / APPROVED). The uploaded receipt files follow immediately after each data sheet: images get their own page, and uploaded PDFs are taken over page by page in their original form — no more “print separately” notes. Every page carries a footer with the receipt number and page count, so nothing can be swapped or lost unnoticed.

The report is assembled in the browser. No PDF library has to be installed on the server, and the receipt files never leave your website.

Selectable reference date
The period filter can be based on the submission date, the approval date, the payment date, or the receipt date. Treasurers usually audit by payment date; the tax adviser may want the receipt date. The plugin warns you when receipts are missing the chosen date and would silently drop out of the report.

Emails that match the task

The board receives the amount, the key details, the receipts and two buttons — approve or reject. The treasurer receives everything needed to make the transfer, with the payment reference in its own box ready to be copied into a banking app. That reference starts with the receipt number, and that is not cosmetic: it is what the bank reconciliation looks for later when matching the statement against the receipts. Emails are sent by the plugin itself rather than through the Contact Form 7 template, so a hand-edited template can no longer break the approval links. Set the scout_send_own_emails filter to false to go back to the Contact Form 7 mail.

Bank statement reconciliation
Upload your bank export as CSV and the plugin answers the central audit question: is every debit backed by a receipt? Matching runs on amount, receipt number in the payment reference, name, IBAN suffix, and date proximity; split transfers covering two receipts are detected as well. Anything ambiguous is offered as a choice instead of being guessed. The result can be exported as a CSV that keeps all original columns and adds a Belegnummer (receipt number) column, so every line of the bank statement is traceable during the audit. A printable reconciliation protocol with signature lines is included.

The CSV is read and processed entirely in your browser — it is never uploaded to the server or sent anywhere. Common German bank exports work out of the box (semicolon or comma, UTF-8 or ISO-8859-1, German or English number and date formats, preamble lines before the header), and every column can be remapped by hand if a format is unusual.

Category statistics
The backend includes a statistics view that breaks down expenses by category (e.g. “Travel”, “Materials”, “Camp costs”) and shows the annual total. Data is cached to keep database load minimal.

Retrospective file upload
Administrators can add further receipts to an existing submission via the backend — for example when a paper receipt arrives by post.

Security

  • All form inputs are sanitized before database storage
  • All output is context-appropriately escaped (esc_html, esc_attr, esc_url)
  • Admin actions are protected with WordPress nonces
  • Separate single-use tokens per stage, stored as HMAC-SHA-256 hashes, valid for 60 days
  • State changes only via POST; automated GET requests cannot trigger an approval
  • Receipts delivered through a permission-checked endpoint, never by direct URL
  • Signed receipt links with an expiry date for board and treasurer e-mails
  • Form input is validated for plausibility, not just sanitized (amount, dates, participants, IBAN suffix)
  • Uploads are all-or-nothing: if one file fails, no orphans are left behind
  • File content is checked against the file extension
  • CSV exports neutralize spreadsheet formulas without altering amounts
  • Database queries exclusively via $wpdb->prepare()
  • File upload exclusively via wp_handle_upload() — no direct use of move_uploaded_file()
  • Allowed file types are server-side restricted to JPG, PNG, GIF, and PDF
  • Maximum file size: 10 MB per file

Requirements

  • WordPress 6.5 or later (this is when core started enforcing the Requires Plugins dependency on Contact Form 7)
  • PHP 7.4 or later
  • Contact Form 7 (must be installed and active)
  • A configured reimbursement form in Contact Form 7 with the correct field names

Supported Languages

Interface strings run through __() and _e() with the text domain scout-reimbursement-pro. The source language is German; a complete English (en_US) translation is included and covers all 363 strings, including the report tab, the bank reconciliation and the security notices. A .pot file ships alongside it for further translations.

The Contact Form 7 form created on first activation is generated in the language active at that moment; changing the site language later does not rewrite an existing form. Use “Reset the form to the template” in the settings if you want it regenerated.

Privacy

The plugin stores the submitter’s name, e-mail address, purpose and period of the expense, the amount, the last four digits of the IBAN, and the uploaded receipts.

It hooks into the WordPress privacy tools: a suggested paragraph is offered for your privacy policy, and the personal data exporter and eraser both cover reimbursement records. Paid submissions are reported as retained rather than deleted, because they are accounting records subject to statutory retention periods. How long those periods run depends on the legal classification of the record, so the plugin does not name a fixed number of years. If you want paid submissions erased automatically once a period you define has passed, set it with the scout_retention_years filter. Submissions that are still open or were rejected are erased along with their receipt files.

The plugin does not send data to any external service of its own. It has no telemetry, no CDN calls and no external API. Notification emails go out through whatever mail infrastructure your WordPress is configured to use, so if you have an SMTP or transactional mail plugin in place, reimbursement details pass through that provider — worth listing in your processing records. The audit report and the bank reconciliation are built inside your browser; the bank CSV is never uploaded anywhere.

Third Party Libraries

This plugin bundles pdf-lib 1.17.1 (https://github.com/Hopding/pdf-lib), used in the browser to build the audit report and to embed uploaded receipt PDFs. pdf-lib is released under the MIT License; the full licence text is included in assets/vendor/pdf-lib-LICENSE.md. The library is loaded from your own server — no external CDN is used and no data is sent anywhere.

Screenshots

Installation

  1. Upload the plugin folder scout-reimbursement-pro to the /wp-content/plugins/ directory.
  2. Activate the plugin through the “Plugins” menu in the WordPress backend.
  3. Make sure Contact Form 7 is installed and active.
  4. Go to Pfadi-Kasse Settings and enter:
    • The board’s email address (receives new submissions and approval links)
    • The treasurer’s email address (receives payment instructions after approval)
    • The Contact Form 7 form ID for reimbursements
  5. Create a Contact Form 7 form with the fields your-name, your-email, your-subject, art, teilnehmer, von, bis, Betrag, IBAN, and your-message.
  6. Add the tags [approve_link] and [reject_link] to the Contact Form 7 email template — the plugin populates these automatically with secure action links.
  7. Embed the form on any WordPress page using the shortcode.

After configuration the plugin is fully ready to use.

FAQ

The board wants to approve with one click. Does that still work?

Yes. Clicking the link in the e-mail is still all it takes. The link now opens a short page that submits the approval itself, so the person sees the confirmation right away. The reason for the extra step is that many mail providers and security gateways open every link in a message to scan it — with the old design, such a scan could approve a reimbursement nobody had looked at. Those scanners do not run the page, so they can no longer trigger anything. Without JavaScript a button appears instead.

Can I make the approval link stricter?

Yes. Settings offers a two-click mode: the link shows the booking and waits for a button press. The default single click already stops ordinary link scanners, because the change only happens over POST from the confirmation page. Two clicks additionally cover mail sandboxes that render pages with JavaScript.

An approval link has expired. What now?

Open the submission in the backend and use “Send new link”. That issues a fresh link to the board or the treasurer and invalidates the old one. Links are valid for 60 days; the scout_token_lifetime filter can change that.

Are receipts publicly accessible?

No. They are delivered by an endpoint that checks permission first, and the storage directory is closed off. Board and treasurer receive signed links that expire. On Nginx, where .htaccess is ignored, the settings page tells you whether the directory is still reachable and shows the rule to add.

Is my bank statement uploaded anywhere?

No. The CSV is read by your browser and compared against the receipt data there. It is never sent to the web server, to Anthropic, or to any other service. Closing the tab discards it; only your manual assignments are kept in your browser’s local storage so a reload does not lose your work.

Which bank exports work?

The importer handles the common German formats: semicolon or comma separated, UTF-8 or ISO-8859-1, amounts like -1.234,56 or 1234.56-, dates as 15.03.2026 or 2026-03-15, and account information printed above the actual header row. Separate debit/credit indicator columns (S/H) are supported. If a column is detected incorrectly, you can remap every field by hand and the evaluation updates immediately.

How does the plugin decide which receipt belongs to which debit?

First by amount. Among the receipts with a matching amount it scores the receipt number appearing in the payment reference, the submitter’s name, the IBAN suffix, and how close the booking date is to the approval or payment date. A receipt is assigned automatically only when it is clearly ahead of the alternatives; otherwise the transaction is marked as needing a decision and you pick from a ranked list. Nothing is ever silently guessed.

Why is a debit shown as “ohne Beleg” even though a receipt exists?

Usually the amounts differ — for example because the bank booked a fee alongside, or two receipts were paid in one transfer. Split transfers covering two receipts are detected automatically; for anything else, raise the amount tolerance in the reconciliation settings or assign the receipt by hand.

Can I still print the report the old way?

Yes. The report tab still offers the HTML print view. It is useful for a quick look, but it can only link to uploaded PDFs — for a report where the PDFs are actually part of the document, use the PDF generation.

Does the PDF generation need anything installed on the server?

No. The report is assembled in the browser using the bundled pdf-lib library, so no PHP PDF extension and no external service is involved. Very large reports simply take a little longer; a progress bar shows what is happening.

What happens if a receipt file is missing or damaged?

The report still gets built. The affected receipt’s data sheet marks the file as missing, the log below the button names it, and the report tab shows a warning beforehand. A damaged or encrypted PDF is skipped with a note rather than aborting the whole report.

Do I need a special hosting environment?

No. The plugin works on any standard WordPress hosting with PHP 7.4+ and wp_mail() enabled. An Apache web server is recommended for the .htaccess protection of the upload directory. On Nginx servers the directory protection must be configured manually.

Where are the receipt files stored?

Files are stored in the WordPress uploads directory under wp-content/uploads/scout-receipts/, organised by year and month. The directory is protected from direct access by an .htaccess file.

Can users without a WordPress account submit requests?

Yes. The submission form is a public Contact Form 7 form and requires no login. Only the approval and payment links are protected by secure tokens.

What happens if someone tries to use a link twice?

The plugin prevents duplicate status changes server-side. An approval link can only approve a submission once — on the second click an informational message is displayed. The same applies to rejections and payment confirmations.

Can I use the plugin without Contact Form 7?

No. The submission process is tightly coupled to Contact Form 7. The plugin is designed as a CF7 extension and requires its submission API.

Are emails sent via WordPress core functions?

Yes, exclusively via wp_mail(). For reliable email delivery we recommend an SMTP plugin such as “WP Mail SMTP”.

Is there an export function?

The plugin currently provides a print-optimised annual report (HTML with print stylesheet). A CSV export is planned for a future release.

How are receipt numbers assigned?

Receipt numbers are assigned automatically and sequentially when a submission is approved by the board. Numbering starts at N-1 and continues across all years. The numbering cannot be changed manually.

Is the plugin GDPR-compliant?

The plugin stores personal data (name, email, IBAN suffix) in the WordPress database. Operators must reference this processing in their privacy policy. The plugin itself does not transmit data to any external server.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Scout Reimbursement Pro” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

1.3.0

Features:
* Audit report as a single PDF — receipt PDFs are embedded page by page directly after each data sheet, images get their own page
* Freely selectable report period with a choice of reference date (submission, approval, payment, or receipt date)
* Cover sheet with totals, category breakdown and signature lines, plus an index of all receipts with page numbers
* Footer with receipt number and page count on every page, including imported and rotated receipt pages
* Bank statement reconciliation — compare a bank CSV against the recorded receipts and see which debits have no receipt
* CSV export with an added “Belegnummer” column for traceable auditing, plus a printable reconciliation protocol
* Detection of split transfers covering two receipts, and manual assignment for ambiguous cases
* Warning when receipt files are recorded in the database but missing on the server
* Notification emails are designed for the job at hand: the board gets the amount, the details and two large approve/reject buttons; the treasurer gets a ready-made payment reference to copy straight into a banking app, the payee, the IBAN ending as a check, the receipts and a single “mark as paid” button
* The suggested payment reference starts with the receipt number, which is what lets the bank reconciliation match the transaction to the receipt later. Typographic characters banks reject are replaced and the text is kept within the 140 character SEPA limit
* All emails go out as HTML with an equivalent plain-text version
* Complete English translation of all 363 strings, plus a .pot file for further languages
* PDF assembly and CSV reconciliation run entirely in the browser

Security:
* Receipts are no longer reachable by direct URL — delivery runs through a permission-checked endpoint, with signed, expiring links in notification emails
* Separate single-use tokens for approval and payment, stored as hashes, so an approval link can no longer confirm a payment
* Approval runs via POST behind a self-submitting confirmation page — one click from the email as before, but mail scanners, link previews and prefetching can no longer trigger it. A two-click mode is available in the settings
* Uploaded files get a random name component and are checked for content matching their extension
* Uploads are all-or-nothing: a failed file no longer leaves earlier files orphaned on the server
* File names in the upload list are inserted as text, closing a DOM XSS path
* CSV exports neutralize spreadsheet formulas while leaving negative amounts intact
* Form input is validated for plausibility (amount, dates, participants, IBAN suffix with leading zeros), not only sanitized
* Privacy policy text plus personal data exporter and eraser, with retention periods respected for paid submissions

Emails:
* Notification emails are HTML with a plain-text alternative, sent by the plugin rather than through the Contact Form 7 template
* The treasurer email contains a copy-ready payment reference starting with the receipt number, so the bank reconciliation can match the transaction later
* Board and treasurer emails use large buttons for the action they need to take

Bookkeeping:
* Receipt numbers are protected by a unique index and are never reused; a rejected submission keeps its number. Existing duplicates are reported with a repair action instead of being silently renumbered
* Two parallel approval requests can no longer both succeed, and rejection from an email link cannot undo an approval that happened in parallel
* Dates use the WordPress time zone consistently

Other:
* Settings page shows whether receipts are still reachable from outside and provides the Nginx rule
* Backend action to send a fresh approval or payment link when one has expired
* Activation no longer overwrites an existing Contact Form 7 form; a deliberate reset button was added to the settings
* Front-end styles and scripts are only loaded on pages that actually contain this plugin’s form, and no longer restyle other Contact Form 7 forms
* Requires WordPress 6.5 or later

1.2.0

  • New: Submissions can be rejected in the WordPress backend even after approval (e.g. for duplicate receipts)
  • New: Optional rejection reason is included in the notification email to the applicant
  • New: Confirmation dialog before admin rejection prevents accidental actions
  • Compatibility: Tested with WordPress 7.0 (“Armstrong”)

1.1.0

  • New: English translation (en_US) for all frontend and backend strings
  • New: Upload button labels fully internationalized
  • Improvement: Upload button labels now passed via wp_localize_script (translatable)

1.0.0

  • Initial release
  • Complete submission and approval workflow
  • Mobile-optimised file upload with client-side image compression
  • Secure file storage with .htaccess protection
  • Email notifications for all parties involved
  • Sequential receipt numbers
  • Annual report with print function
  • Statistics breakdown by category and year