007AJ FillDesk
Version 1.0.2 · Requires WordPress 6.3+ and PHP 7.4+ with the mbstring, zlib and GD extensions · GPLv2 or later
FillDesk adds an online PDF editor to your site. Visitors upload a PDF or open a form from your library, fill, sign and edit it in the browser, and get a free watermarked preview. The clean file is sold per download, as a credit pack or as a time pass, through Stripe Checkout. You don't need a shop plugin.
1. Installation
- Go to Plugins > Add New > Upload Plugin, choose
filldesk-1.0.2.zip, then install and activate it. - On activation FillDesk first checks that the PHP extensions it needs (mbstring, zlib and GD) are enabled. If one is missing, it is not activated and you see which one to ask your host for. See Server requirements.
- On activation FillDesk creates an Edit PDF page (
/edit-pdf/) that contains[filldesk]. It also creates its database tables and a private storage folder. - Open FillDesk > Tools. The system check should be green: HTTPS, PDF engine, a writable storage folder, and the upload limit.
- Optionally, import the two sample forms (Weekly Timesheet with 15 fillable fields, and Simple Invoice) from the same screen.
2. Connecting Stripe
- In your Stripe Dashboard, copy your secret key. Start in test mode (
sk_test_…). - Go to FillDesk > Settings > Payments, choose a mode and paste the key. Saved keys are shown masked.
- Click Create the webhook in Stripe for the current mode. FillDesk registers
/wp-json/filldesk/v1/stripe/webhookforcheckout.session.completed,checkout.session.async_payment_succeeded,charge.refundedandcharge.dispute.created, and saves the signing secret. If you created the endpoint with version 1.0.0, addcharge.dispute.createdto it in the Stripe dashboard (or create it again) so disputes revoke access too. You can also create the endpoint yourself and paste itswhsec_…secret. - Test a purchase with the card
4242 4242 4242 4242, any future expiry date and any CVC. When everything works, switch the mode to Live and repeat steps 2 and 3 with your live key.
The signed webhook is the source of truth. Each event is verified against the Stripe-Signature header and processed only once. An event counts as processed only when its changes are saved: if the database fails, the webhook answers with an error (HTTP 500), nothing is half-saved, and Stripe sends the event again later. FillDesk > Tools shows webhook failures from the last 7 days. If a webhook is delayed, the buyer's page asks the plugin to check the Checkout session directly, so the download still unlocks. Card data is entered on Stripe's hosted page and never touches your site.
3. Pricing
| Product | Default | What the buyer gets |
|---|---|---|
| Single download | $2.99 | Clean downloads of one document. Further edits and re-downloads are free for the configured hours. |
| Credit pack A / B | 5 for $9.99 / 20 for $29.99 | One credit per document. Credits don't expire. Re-exporting the same document doesn't use another credit. |
| Time pass | 7 days for $4.99 | Unlimited clean downloads until the pass ends. It does not renew. |
Each product can be switched off. You can override prices per form in the form's FillDesk box, set the currency and the checkout note, offer the first download free, and require an account before purchase. Credit packs and passes need a logged-in buyer. Single downloads also work for guests.
4. Form library and landing pages
Under FillDesk > Forms, each form is a post with a PDF attached. When you choose the PDF, FillDesk normalizes it in the browser, detects its fillable fields and creates a thumbnail. Each form gets an SEO landing page at /forms/your-form/. The page has a title, meta description, thumbnail, an "Edit online" button, how-to steps, an FAQ, related forms, breadcrumbs and structured data. The structured data is skipped automatically when Yoast SEO or Rank Math is active. Forms can be grouped with the Form category taxonomy. The archive base (forms) can be changed in Settings.
Replacing a form's PDF. Library PDFs are versioned. When you upload a new PDF for a form, it is stored as a new version and used for documents opened from then on. Every customer document stays pinned to the exact version it was opened from, so saved edits, signatures and paid documents never move or break when you update a template. An older version is deleted automatically once no document uses it any more (for example after the retention cleanup removes the last one). When you update from 1.0.0, existing documents are pinned to the form's current PDF.
5. The editor
- Annotate: text (three fonts, size, color, bold), date, sticky note, check, cross and dot marks, highlight, whiteout, rectangle, line, arrow, underline, strikeout, freehand pen and images.
- Sign: draw, type or upload a signature or initials. A notice reminds users to check whether an e-signature is accepted for their document.
- Form fields: text, comb, multi-line, checkboxes, radio buttons and dropdowns are filled in place and flattened into the final PDF.
- Pages: rotate, reorder and delete pages.
- Also: undo/redo, zoom, keyboard shortcuts, autosave (work survives a reload), and a large bottom toolbar on phones.
- Preview renders a server-side PDF with the watermark drawn into it. Download opens the paywall, or downloads straight away if the buyer already has access.
Encrypted and XFA-only PDFs are refused. For AcroForms that also carry XFA data, the XFA layer is removed so the standard fields can be filled.
6. Shortcodes and blocks
| Shortcode | Purpose |
|---|---|
[filldesk] | The editor with an upload area. Add ?filldesk_open=slug to the page URL to open a library form. |
[filldesk form="slug-or-id"] | The editor preloaded with a specific form. |
[filldesk_library] | A searchable grid of your forms. |
[filldesk_my_documents] | The buyer's documents, credits, pass and download links. |
The same three are available as blocks: FillDesk Editor, Form Library and My Documents.
7. Purchases, refunds and manual grants
FillDesk > Purchases lists every entitlement with its buyer, product, credits, expiry, amount and status. You can export it as CSV, grant credits or a pass by hand (for support cases or giveaways), and revoke access. A full refund in Stripe revokes the related access automatically through the signed charge.refunded webhook, and a dispute does the same through charge.dispute.created. Partial refunds keep the purchase active.
What revoking does (by refund, dispute or the Revoke link): unused credits are removed, a pass ends, download links created with that purchase stop working, and the purchase can't authorize any new clean PDF. That includes re-exporting a document a credit was already spent on, a free edit inside the free-edit window, and re-downloading the same revision. Revoked purchases show the status refunded (Stripe) or revoked (by hand) and are left out of the revenue total. If Stripe happens to deliver the refund before the payment confirmation, FillDesk remembers it and the purchase is never granted.
If an export can't be recorded. A download is first saved as an unfinished record with a unique token. One database statement then marks it finished and spends the credit, so the two always happen together or not at all. If anything fails, FillDesk reads the record back by its token before cleaning up. If it was finished, the customer gets the download and was charged once. If it wasn't, nothing was spent, the unfinished record and its file are removed, and the customer sees "Your download could not be prepared because of a database error. No credit was used." If the database can't even be read at that moment, FillDesk changes nothing, asks the customer to try again in a minute (they are never charged twice) and logs it. A cleanup task every 15 minutes removes unfinished records left behind by a crash. Problems show up in the system check under FillDesk > Tools.
8. Storage, security and retention
- Files are stored outside the web root when the server allows it, otherwise in
wp-content/uploads/filldesk-<random>/. File names are random, and every file begins with a PHP exit guard, so a direct request returns nothing. - Apache is protected by an
.htaccessfile. On nginx, add the rule shown under FillDesk > Tools, for example:location ^~ /wp-content/uploads/filldesk-xxxx/ { deny all; return 403; } - Uploads are checked by their content (PDF signature, size and page limits). The size is also capped by your PHP
upload_max_filesize. - Downloads use short-lived signed links. A tampered link returns 403 and an expired one returns 410. Every document belongs to an account or to a private, signed browser cookie, so one visitor can't open another visitor's file.
- Uploads and previews are rate-limited per IP address (taken from
REMOTE_ADDR). - Drafts, exports and guest documents are deleted automatically after the hours you set. Cleanup runs every 15 minutes.
- FillDesk supports WordPress's personal data export and erase tools.
9. Settings reference
| Tab | Options |
|---|---|
| General | Editor page, library base, who may upload, maximum MB and pages, enabled tools, brand color, date format |
| Pricing | Currency, products and prices, pack sizes, pass days, re-download and free-edit hours, first download free, require an account, checkout note |
| Payments | Mode, test/live secret keys, webhook secrets, the create-webhook button, email download link, membership levels |
| Watermark | Text ({site} placeholder), opacity, angle, density, footer line |
| Privacy | Retention hours, consent text, link lifetime, single-use links, rate limits, delete data on uninstall |
10. Developer hooks
Filters: filldesk_storage_path, filldesk_price, filldesk_allowed_tools, filldesk_watermark_text, filldesk_retention_hours, filldesk_rate_limit, filldesk_can_export, filldesk_membership_has_access, filldesk_stripe_session_params, filldesk_landing_template, filldesk_form_block_editor.
Actions: filldesk_document_created, filldesk_previewed, filldesk_paid, filldesk_exported, filldesk_refunded.
Membership plugins can grant download access through filldesk_membership_has_access. If Paid Memberships Pro is active, members of the level IDs listed under Settings > Payments > Membership levels download for free.
filldesk_missing_extensions filters the list of missing PHP extensions (mainly useful for testing the requirement notice).
11. FAQ and troubleshooting
The payment went through but the download didn't unlock. Check FillDesk > Tools for the time of the last webhook. Make sure the endpoint exists in Stripe for the current mode and that its secret matches. The page also checks the session directly after a few seconds.
The system check shows webhook failures or unfinished downloads. Both mean the database refused a write. Webhook events that failed were answered with an error and Stripe retries them automatically (you can also resend them from the Stripe dashboard). Unfinished downloads never cost the customer a credit and are removed by the cleanup task. If the counts keep growing, check your database server's error log and free disk space.
Uploads fail on larger files. Your PHP upload_max_filesize and post_max_size limit the upload size. The system check shows the effective limit.
Do signatures count as legal signatures? FillDesk places a signature image. Whether that is accepted depends on the document and your jurisdiction.
Do flattened forms keep their field borders? Field values are drawn into the page. The visual styling of interactive widgets isn't carried into the clean file.
12. Uninstalling
By default, deleting the plugin keeps your data. If you enable Delete data on uninstall under Settings > Privacy first, deleting the plugin removes its tables, options, forms, the Edit PDF page, user meta, scheduled tasks and the storage folder.
13. Server requirements
- WordPress 6.3 or newer, PHP 7.4 or newer, MySQL 5.7+ or MariaDB 10.3+ (InnoDB tables, the WordPress default).
- PHP extensions: mbstring (text handling in the PDF engine), zlib (reading and writing compressed PDF streams) and GD (signatures, images and form preview thumbnails). Most hosts enable all three by default.
- HTTPS for live payments.
FillDesk checks the extensions at activation and on every page load. If one is turned off later, the plugin doesn't crash: an error notice in the admin names the missing extension, the editor shortcodes show "The PDF editor is temporarily unavailable", and the editor, previews and new downloads are switched off. Purchases, refund webhooks and existing download links keep working, and no data is lost. Everything comes back once the extension is enabled again. FillDesk > Tools shows the status of each extension.
14. Changelog
1.0.2
- Fixed: refund, dispute and payment webhooks are processed in one database transaction together with the record that marks the event as done. A database failure is rolled back and answered with HTTP 500 so Stripe retries the event; retries are safe because revoking is idempotent.
- Fixed: downloads are saved as unfinished records with a unique token and finished by one statement that also spends the credit. After any failure the record is read back, so a finished download is never deleted and an unfinished one never costs a credit. Every cleanup step is checked, and a cleanup task settles unfinished records left by a crash.
- Fixed: deleting documents, retention cleanup, privacy erasure and replacing a form PDF check their database writes and remove files only after the database change succeeded.
- New: webhook failures and unfinished downloads are shown in the system check.
1.0.1
- Fixed: a credit already spent on a document could authorize another clean export after the purchase was refunded or revoked. Every path that allows a clean PDF now requires the purchase to still be active, and download links check it again when opened.
- Fixed: replacing a library form's PDF changed the PDF used by existing customer documents. Library PDFs are now versioned and each document is pinned to its version. Documents from 1.0.0 are pinned on upgrade.
- Fixed: the export record is written in a database transaction with the credit being spent. On failure the credit is restored, the file is deleted and a clear error is shown.
- New: mbstring, zlib and GD are declared and checked at activation and on every load, with an admin notice instead of a PHP fatal error.
- New: Stripe disputes revoke the purchase. A refund that arrives before the payment confirmation is remembered.
- Improved: failed database writes when saving edits, adding images, granting or revoking purchases are reported instead of ignored.
1.0.0 First release.
Credits
FillDesk bundles PDF.js (Apache-2.0), @cantoo/pdf-lib (MIT), FPDI (MIT), tFPDF (LGPL), DejaVu fonts and Dancing Script, Great Vibes and Caveat (SIL OFL 1.1). Their notices ship inside the plugin folder.