EP Holiday Bookings
EP Holiday Bookings is a purpose-built plugin for independent travel agents. Not a DIY booking engine — an enquiry-first workflow that matches how small agents actually work. Package listings attract interest, enquiries land in a pipeline, the agent quotes, collects deposits, then balances, and tracks traveller details securely.
Published by ElmsPark Studio.
Who this is for
Section titled “Who this is for”Independent travel agents who:
- Curate a catalogue of packages rather than selling from live inventory.
- Talk to every customer before confirming a booking (enquiry-first, not self-checkout).
- Take deposits upfront and balances closer to travel.
- Need to hold traveller details (passport numbers, dietary requirements, emergency contacts) securely.
Not for online travel agents with API-based inventory. Not for ATOL-bonded trust-account operations — that’s a compliance domain this plugin doesn’t touch.
Features
Section titled “Features”Destination and package catalogue
Section titled “Destination and package catalogue”- Destinations with name, region, country, hero image, description.
- Packages tied to a destination with dates, pricing, capacity, itinerary, inclusions, exclusions.
Enquiry-first flow
Section titled “Enquiry-first flow”A customer browses packages, finds one they like, submits an enquiry form. The agent replies, discusses, quotes. When the customer commits, the agent turns the enquiry into a booking.
Pipeline
Section titled “Pipeline”Enquiry statuses: New → Contacted → Quoted → Booked → Cancelled. Visible on the admin dashboard so the agent always knows what needs attention.
Deposits and balances
Section titled “Deposits and balances”Stripe-integrated payment collection:
- Deposit when the booking is confirmed.
- Balance closer to travel (typically 6 to 8 weeks before).
- Payment links sent to the customer, not self-checkout on the site.
Travellers
Section titled “Travellers”Per-booking traveller records:
- Name, date of birth, passport number, nationality.
- Passport numbers encrypted at rest using AES-256.
- Dietary requirements, emergency contact.
Encrypted sensitive fields, consent logging via EP GDPR, full-record deletion on data subject erasure request.
Shortcodes
Section titled “Shortcodes”| Shortcode | Purpose |
|---|---|
[holiday-packages] | Full package catalogue with filtering. |
[holiday-package slug=spain-villa-week] | Single package page. |
[holiday-destinations] | Destination catalogue. |
[holiday-enquiry package=spain-villa-week] | Enquiry form for a specific package. |
[holiday-search] | Search interface across packages. |
Requirements
Section titled “Requirements”- PageMotor 0.8.2b or later
- EP Email (required for notifications)
- EP Suite base class
- Stripe account (for deposit / balance collection)
Optional:
- EP GDPR for consent logging.
- EP Newsletter for opt-in at enquiry time.
Status
Section titled “Status”Version 0.3.12: the core pipeline and catalogue are stable. Some UX polish items are still pending. Details in the plugin’s own CHANGELOG.
Installation
Section titled “Installation”- Install EP Email.
ep-holiday-bookings.zipcomes with an EP Suite licence — ElmsPark supplies it directly (see EP Suite plugins); after install it updates through your site’s Updates screen.- Upload via Plugins → Manage Plugins. Activate.
- Configure Stripe keys in the settings.
- Work through Destinations → Packages → Test enquiry form.
Database tables
Section titled “Database tables”All prefixed {prefix}ep_holiday_:
destinations— destination catalogue.packages— holiday listings.enquiries— customer enquiries with pipeline status.bookings— confirmed bookings with deposit/balance tracking, Stripe payment intent IDs.travellers— per-booking traveller details, passport encrypted.customers— customer database.
Compliance notes
Section titled “Compliance notes”ATOL and ABTA
Section titled “ATOL and ABTA”This plugin does NOT handle ATOL bonding, client-money trust accounts, or ABTA-specific compliance. If your regulatory context requires those, use accounting and compliance tools designed for that purpose. EP Holiday Bookings is operational CRM, not financial infrastructure.
Passport storage
Section titled “Passport storage”Passport numbers are encrypted at rest using AES-256 with a key stored outside the database. Decryption happens only server-side, only when the agent explicitly views a traveller record. The key itself should be:
- Stored in a PHP environment variable (
EP_HOLIDAY_ENCRYPTION_KEY). - Backed up separately from database backups.
- Rotated periodically.
If you lose the key, stored passport numbers become unrecoverable. This is by design.
GDPR erasure
Section titled “GDPR erasure”On a data subject erasure request, the plugin:
- Deletes the customer record.
- Deletes all associated enquiries and bookings.
- Deletes traveller records with encrypted passport numbers.
- Retains a minimal audit row noting the erasure happened.
Troubleshooting
Section titled “Troubleshooting”“Enquiry form submitted but I didn’t get notified”
Section titled ““Enquiry form submitted but I didn’t get notified””Check EP Email config. Enquiry notifications go through EP Email’s queue.
“Deposit payment link is generated but the customer can’t pay”
Section titled ““Deposit payment link is generated but the customer can’t pay””Check Stripe keys are for the correct mode (test vs live). Check the payment link hasn’t expired — Stripe links have configurable lifetimes.
“Passport number shows ‘decryption failed’”
Section titled ““Passport number shows ‘decryption failed’””The encryption key has changed since the record was encrypted. Either restore the old key, or re-collect the passport from the customer.
“Package isn’t appearing in the catalogue”
Section titled ““Package isn’t appearing in the catalogue””Check status is Active and dates haven’t expired. Packages past their end date are auto-hidden by default.
“Enquiry pipeline status doesn’t update when I mark an enquiry as Booked”
Section titled ““Enquiry pipeline status doesn’t update when I mark an enquiry as Booked””Creating a booking from an enquiry should auto-advance the enquiry to Booked. If it doesn’t, there’s a likely bug — log in the review queue with an enquiry ID.
Feedback and corrections
Section titled “Feedback and corrections”For a quick question about this plugin, EP Support inside your admin is the fastest option. The chat widget sits on every EP plugin settings page and knows which one you’re on, with starter questions and links preloaded for that exact screen.
For anything bigger — a bug report, a feature request, or a “how do I…” that needs a real reply — open a ticket at help.elmspark.com. A real person, helped by AI, writes the reply. Usually within a few hours. Tickets don’t disappear into the void.
Changelog
Section titled “Changelog”0.3.30
Section titled “0.3.30”- The traveller details page and the enquiry form’s new message are translated. Customers whose site runs in German, Spanish, French, Italian, Dutch or Portuguese now see the traveller details page (headings, every field, the notes, the button and the error messages) in their own language. Before, it was in English only.
- The traveller details page now tells browsers and screen readers which language it is in, so a screen reader pronounces the words correctly.
0.3.29
Section titled “0.3.29”- Behind the scenes: the shared ElmsPark files this plugin carries are brought up to date in its source, matching what the installed plugin already contained. Nothing changes that you can see.
0.3.28
Section titled “0.3.28”- The enquiry form is protected from floods. Up to five enquiries an hour can come from the same connection, which is plenty for a family asking about several holidays. A sixth is turned away with a polite message asking them to wait or call you, and nothing is saved.
- To do this the plugin keeps a scrambled fingerprint of each enquirer’s connection, never the address itself, and forgets it after a day.
0.3.27
Section titled “0.3.27”- Balance reminders are sent. Each customer with a deposit paid now gets one reminder when their balance falls due within 14 days. Before, this email was only ever sent when you created a balance payment link by hand.
- Departure reminders are sent. Each customer with at least the deposit paid gets one reminder before they travel, using your “Departure Reminder (Days Before)” setting (7 days unless you change it). Before, the setting did nothing.
- Passport numbers are removed 30 days after a holiday returns, once a day, as the plugin always intended. Before, nothing ever ran the removal.
- These run every hour through EP Cron if you use it; otherwise, at most once an hour when you open the admin. A reminder that cannot be sent is tried again next time, and none is sent twice.
- Two bookings made at the same moment no longer clash. Each booking reference is unique, and when two bookings were made at the same instant the second could fail. It now simply takes the next reference.
- When EP Email is installed, emails sent outside a normal page view (the reminders, and Stripe payment emails) now go through EP Email rather than falling back to PHP mail().
0.3.26
Section titled “0.3.26”- A tighter security check on admin actions. Every admin action (managing destinations, holidays, customers, enquiries and bookings) now needs the plugin’s own security token. Before, a request that sent no token at all skipped that check and relied on PageMotor’s own check alone. Nothing changes that you can see.
0.3.25
Section titled “0.3.25”- Keep sold out now tells you what it did, and can be undone. After you choose Keep sold out on the dashboard, a message names the holiday and confirms it will stay sold out. An Undo link puts it back on the list.
- Clearer for screen readers. Each Keep sold out link now names its holiday, so a list of them no longer reads as the same words repeated.
- Safer links. The Keep sold out and Undo links no longer carry the site’s security token in the address. Each link now works only for its own holiday and its own action.
0.3.24
Section titled “0.3.24”- The dashboard notices now fit on a phone. The notices for payments made twice, and for sold-out holidays with places free, could run off the right-hand edge of a phone screen. This happened when a holiday title or a Stripe payment reference was a single long word, and the whole admin page then scrolled sideways. Long words now wrap inside the notice. On a wider screen, payment references stay on one line, so they are easy to copy.
0.3.23
Section titled “0.3.23”- Holidays that sold out before the last update are now handled too. 0.3.22 reopened sold-out holidays only when they had been marked after that update. Now, on the first page load after this update:
- Sold out and still full (and not yet departed): treated as marked automatically. When a place comes back, the holiday reopens.
- Sold out with places free: left as it is, because it may be sold out on purpose. The dashboard lists it with the number of places free. Set its status to Active to put it back on sale, or choose “Keep sold out” and it is not listed again.
- A holiday that has already departed is never put back on sale.
0.3.22
Section titled “0.3.22”- A holiday that sold out now reopens when places come back. When a holiday fills up, the plugin marks it sold out. Until now it stayed sold out even after a booking was cancelled or refunded, or you raised its capacity, so the freed places could not be booked.
- Now it goes back to how it was before, for example “active”, as soon as it has room again.
- A holiday you marked as sold out yourself is never reopened. If you change the status of an automatically sold-out holiday yourself, your choice is kept.
- Holidays that were marked sold out automatically before this update are not reopened automatically. Set them back to active yourself if they have room.
0.3.21
Section titled “0.3.21”- If the refund you made to settle a double payment fails, the dashboard tells you again. When a customer has paid a deposit or balance twice, you refund one of the two payments and the notice goes. If Stripe then fails that refund (a closed card, for example), or you cancel it, the customer has paid twice again. The notice now comes back, naming the payment. Before, nothing showed.
- The booking itself is not changed: its status, amounts and places stay as they are.
0.3.20
Section titled “0.3.20”- You are now told when a customer has paid a deposit twice. This happens, for example, when two payment links for the same deposit are both paid, or when a deposit is paid again after the booking was refunded. The plugin’s dashboard now shows a notice naming the booking, its status and the payment to refund in Stripe if it is not owed. Before, this was only written to the error log.
- The notice goes once either payment is refunded in full. The booking’s status and places stay as they are.
- Fixed: refunding one of the two deposit payments used to mark the whole booking as refunded and put its places back on sale, even though the other payment was kept. Now the booking stays booked.
0.3.19
Section titled “0.3.19”- You are now told when a customer has paid their balance twice. This can happen when a balance refund fails after the customer has already paid again, or when two payment links for the same balance are both paid. The plugin’s dashboard now shows a notice naming the booking and the payment to refund in Stripe. Before, this went unnoticed unless you checked the error log.
- The notice goes once either payment is refunded in full. The booking stays fully paid either way.
- Fixed: refunding the newer of the two payments used to mark the balance as owing again, although the other payment was kept. The booking now stays fully paid.
0.3.18
Section titled “0.3.18”- A refund that fails now puts the booking back. Stripe sometimes fails a refund days later (for example, when the customer’s card has been closed), and you can cancel a refund while it is still pending. Either way the money stays with you. Until now the booking stayed marked as refunded and its places stayed back on sale.
- Deposit refund fails: the booking goes back to how it was, and takes its places on the holiday again. If the holiday has filled up in the meantime, the booking is still restored (the customer has paid), and a note in the error log tells you to find the places or refund again.
- Balance refund fails: the booking is fully paid again.
- Only bookings changed by a Stripe refund are put back. A booking you marked as refunded yourself is never changed. No email is sent to the customer.
- What to do: add the event
charge.refund.updatedto this site’s webhook in your Stripe Dashboard, alongsidepayment_intent.succeededandcharge.refunded. The Stripe settings list all three.
0.3.17
Section titled “0.3.17”- Fixed: Stripe payments and refunds never reached your bookings. Stripe tells your site about a payment by sending a message to a web address. There was no address it could use: its messages arrived as ordinary page visits, and nothing was recorded. Deposits and balances paid by card stayed “pending” until you updated them by hand.
- There is now a proper webhook address for this. The Stripe settings show it, with the events to choose.
- What to do: in your Stripe Dashboard, go to Developers, then Webhooks, and add an endpoint with the address shown under Settings, Stripe. Choose
payment_intent.succeededandcharge.refunded, then copy the endpoint’s signing secret into Stripe Webhook Secret. Without the signing secret, nothing is accepted.
0.3.16
Section titled “0.3.16”- Refunds you make in Stripe now update the booking. Before, a refunded deposit or balance left the booking exactly as it was: still showing as paid, still holding its places on the holiday, and still counted in “balance due soon”.
- Deposit refunded in full: the booking is marked refunded and its places go back on sale.
- Balance refunded in full, deposit kept (as many cancellation terms allow): the booking goes back to “deposit paid”, with the balance owing again. Its places stay held.
- Part of a payment refunded (a goodwill amount, or cancellation charges kept): nothing changes automatically. It is noted in the site’s error log for you to review.
- Deposit refunded while the balance is still paid: nothing changes automatically; this is also noted for you.
- No email is sent to the customer; you stay in charge of what you tell them.
- To turn this on, add the event
charge.refundedto this site’s webhook in your Stripe dashboard, next topayment_intent.succeeded. The Stripe Webhook Secret setting now says so.
0.3.15
Section titled “0.3.15”- Fixed: the holiday pages’ own styling and scripts never loaded. The plugin asked for its stylesheets and scripts through a setting that did not exist, so PageMotor was never given them. The enquiry form, the package pages and the search box showed without their designed layout.
- The enquiry form reloaded the page on sending and showed raw data instead of the thank-you message.
- The search button did nothing.
- Now the styling loads. The enquiry form sends without leaving the page and shows the thank-you (or what needs correcting) under the button. The search button takes visitors to the matching holidays.
0.3.14
Section titled “0.3.14”- Fixed: customers could not send their traveller details, and the emailed link showed nothing. The “Complete Traveller Details” link now opens a page with one section per traveller on the booking (name, date of birth, passport number and expiry, dietary needs, medical information, emergency contact). Customers do not need to sign in: the link itself proves who they are, and it stops working if the booking is cancelled or refunded. Opening the link again shows what was saved; the passport number is shown only as its last four characters, and leaving it blank keeps the one on file.
- Fixed: with EP Email active, sending an enquiry showed the visitor an error and the agent was never told. The enquiry was saved, so visitors tended to send it again. Enquiry and booking emails now go through EP Email correctly. If EP Email reports a problem, the plugin logs it and sends the email with the server’s own mail instead, and the visitor always gets the thank-you.
- Fixed: the message typed into the enquiry form was saved as “0”. It is now saved and emailed as written. Enquiries sent before this update have lost their message; the rest of each enquiry is intact.
- Security: enquiry emails no longer carry markup typed into the form. Names, phone numbers and messages from the public form are shown as plain text in the emails sent to you and to the customer, so the form cannot be used to send links or formatted content from your address.
- Security: the key protecting passport numbers is no longer kept as a file in the site folder, where some servers would hand it out to anyone who asked for it. The key is now worked out from your site’s own secret, the same way the suite protects Stripe and other keys. The old file’s key is copied into the database first and the file is deleted only after that copy is confirmed, so passport numbers saved before this update stay readable, and they are re-encrypted with the new key the next time you open the admin.
- Security: traveller details are never saved unprotected. Previously, if the key file could not be written, a new key was made every time and the passport numbers saved could never be read again. Now, if no key can be set up, the details are not saved: the customer is asked to contact you, and the Holiday Bookings dashboard says why.
- Security: dietary and medical details are now encrypted too, as passport numbers already were. Details saved before this update are encrypted the next time you open the admin, and still read the same.
- Security: your Stripe secret keys and webhook signing secret are now stored encrypted. Keys saved before this update keep working and are encrypted automatically the next time the admin loads. Nothing to re-enter.
0.3.13
Section titled “0.3.13”- Security: the Stripe notification address accepted forged payments when no webhook signing secret was saved. Anyone could send a made-up “payment succeeded” message and have a booking marked as deposit paid or fully paid, with the payment emails sent. The 0.3.10 notes said this had been fixed, but the fix was never in the plugin. Payment notifications are now refused outright until a signing secret is saved, and the refusal is logged.
- A payment notification must now match the booking. It must be in the site’s currency and for exactly the deposit or balance amount on that booking, or it is refused and logged. The booking reference in the payment is no longer taken on trust.
- Repeated or late payment notifications can no longer push a booking backwards. Stripe can send the same notification more than once and not always in order. A late deposit notice used to move a fully paid booking back to “deposit paid” (so balance reminders could go out again), and could bring a cancelled or refunded booking back. The deposit now moves a booking on only from “pending deposit”, and the balance only from “deposit paid” or “balance due”. The customer’s email goes out once. A payment for a cancelled booking is recorded and logged without reopening it, and a second payment for a stage already paid is logged so you can check whether a refund is owed.
- The Stripe secret keys and webhook secret are now hidden in the settings screen, like a password.
0.3.12
Section titled “0.3.12”- Fixed: installing a second plugin that also takes Stripe payments could take the whole site down. Five plugins in the suite share the same Stripe helper. With any two of them switched on, the second to load failed outright and PageMotor disabled it to protect the site, so turning on a new Stripe-capable plugin silently cost you the one you already had, with nothing obvious to explain it.
- The guard meant to prevent that had never been able to work, for reasons of when the code is read rather than when it runs. It is now written so that it does. Verified by switching two of these plugins on together: previously the site returned an error and one plugin was disabled, now both load cleanly and payment signature checking still works.