# Changelog

All notable changes to Bingo Car Rental are documented here.

---

## [1.0.59] – 2026-07-07

### Changed
- **wp.org Guideline 5 re-flag (round 2) — extras/discounts were "present but restricted," not absent.** Only the pricing-application logic (`BingoCR_Premium`) was ever stripped from the free build; the extras/discount-code CRUD (`admin_create_extra`, `admin_update_discount`, etc.) and the public `get_extras`/`validate_discount` endpoints lived in the always-shipped `class-bingocr-rest-api.php`, gated by `BingoCR_License::can()`. That's the trialware pattern wp.org explicitly disallows — working code switched off by a flag. Moved all of it, plus PayPal's `paypal_create_order`/`paypal_capture_order`/`paypal_webhook` (same pattern), into a new `class-bingocr-rest-premium__premium_only.php` (`BingoCR_REST_Premium extends BingoCR_REST_API`), stripped from the free build the same way the pricing engine already was. On free installs these routes now 404 — physically absent, not license-gated.

### Security
- Removed the unused `GET /reservations/{id}?email=` endpoint the reviewer flagged as weakly authenticated (numeric ID + email match, enumerable). Confirmed no frontend code called it — dead code, zero-risk removal. A proper token-based lookup is deferred to the planned customer-account (Client Zone) feature, where it can be designed in from the start.
- `paypal_create_order` now also requires the request's `customer_email` to match the reservation's stored email, closing a gap where a REST nonce alone was sufficient to create a PayPal order against an arbitrary `reservation_id`.

## [1.0.58] – 2026-07-04

### Changed
- **readme.txt rewrite for wp.org compliance.** Removed extras, discount codes and PayPal from the free plugin feature list (Pro-only, code absent). Removed the "no recurring fees" line (inaccurate for Pro users). Removed PayPal from External Services (not present in the free build). Added a clear "Pro version" paragraph. Shortened the changelog to relevant entries only.

## [1.0.57] – 2026-06-24

### Changed
- **wp.org Guideline 5 compliance (trialware fix).** Removed all soft gates from the free build: dark theme, custom brand color, and pay-on-site payment methods (cash, card, bank transfer) are now fully available without a Pro licence. Car and location limits removed — free installs are unlimited. The only Pro-gated features that remain are those whose code is physically absent from the free build (extras, discount codes, PayPal / online payments, out-of-hours fee).
- **Admin Settings page:** OOH section replaced with a "Upgrade to Pro" upsell panel when code is absent; Pro badges and disabled state removed from Theme and Primary Color fields.
- **Admin Payment page:** PayPal section replaced with a "Upgrade to Pro" upsell panel when code is absent; Pro badges and disabled state removed from pay-on-site method checkboxes and instructions field.

## [1.0.56] – 2026-06-22

### Fixed
- **PayPal card payment fields now fully reachable in the booking modal.** The PayPal buttons container (including the inline "Debit or Credit Card" form that PayPal expands on click) was in the non-scrollable modal footer, so the card number, expiry, CVV and billing address fields were cut off with no way to scroll to them. Moved the container into the scrollable modal body so users can scroll down to complete card entry.

## [1.0.55] – 2026-06-21

### Fixed
- **Quote endpoint (`/pricing/calculate`) now validates the full date range.** Previously only checked date format, so it could return a price for past dates, a return before pickup, or a duration outside the configured min/max — giving the booking widget a misleading quote. Now applies the same four checks already enforced at reservation creation (return > pickup, no past pickup, min rental days, max rental days) and returns a `400` with a descriptive message for any violation.

## [1.0.54] – 2026-06-18

### Security
- **PayPal amount/currency verification before settlement.** Both `capture_order()` (live) and `handle_webhook()` now compare the captured amount + currency against the recorded payment (via a shared `amount_matches()`, 1-cent tolerance) and refuse to mark a booking paid on mismatch. Closes the gap where the capture docblock claimed an amount re-check that wasn't actually performed (the read-but-unused `$paid_value`).

### Changed
- **Overbooking lock now fails closed on real contention.** `acquire_booking_lock()` distinguishes a lock timeout (genuine contention → the booking returns HTTP 503 "please try again") from a host where the lock is unavailable (degrades to proceeding without it, so bookings are never globally blocked). Timeout reduced to 5s.
- Restored `.distignore` (excluded from the built package by the dotfile filter; used by wp.org SVN deploy tooling).

## [1.0.53] – 2026-06-18

### Security
- **PayPal webhook: closed a forged-settlement path (critical).** `handle_webhook()` only re-verified the capture against PayPal *if the body contained a capture id*, and only checked the webhook signature when a Webhook ID was configured — so a forged `PAYMENT.CAPTURE.COMPLETED` with a known `order_id` (which is exposed to the browser at order-create time) and **no** capture id could reach settlement and mark a booking paid. Now a capture id is **mandatory**, the capture is **always** re-fetched from PayPal and must report `COMPLETED`, and the target reservation is resolved from the **authoritative capture's `custom_id`** (not the request body), with a linkage-mismatch guard. The signature check remains as an extra layer.
- **Reservation creation now validates the full date range.** `create_reservation()` previously checked date *format* only; it now mirrors the availability search — return must be after pickup, no past pickups, and min/max rental length — so a crafted request can't create invalid bookings.
- **Security deposit recomputed server-side.** The submitted `deposit_amount` is no longer trusted; it's derived from the car's deposit settings + global default (matching the frontend), closing the last client-trusted money field.

### Fixed
- **Overbooking race condition.** The availability check and the insert are now serialised per car with a MySQL advisory lock (`GET_LOCK`), so two simultaneous bookings can't both pass the capacity check and overbook a vehicle.
- **Freemius bootstrap no longer fatals without the SDK.** Added the `file_exists()` guard the comment already promised — a missing `vendor/freemius/start.php` now degrades to a free plugin (`bingocr_fs()` returns `null`, handled by `BingoCR_License`) instead of a fatal error.
- Docs: corrected the readme FAQ that still described PayPal as "planned" (it ships in Pro).

## [1.0.52] – 2026-06-18

### Fixed
- **Extras no longer persisted on free reservations (email/record consistency).** `create_reservation` gated the stored `extras` list (and discount input) on `can()` only, while the *total* was gated on the stripped `BingoCR_Premium` class. So on a free install with the `can()` gate edited, the chosen extras were saved on the booking and rendered in the confirmation emails — even though they were never priced into the (correct) total, producing an inconsistent summary (e.g. a back-calculated wrong base). Both `extras` and the discount input are now gated on `class_exists('BingoCR_Premium')`, matching how the total is computed. Result: in the free build the stored record and both emails always match the server-priced total; discounts and out-of-hours were already consistent (derived only through the class-gated `calculate()`).

## [1.0.51] – 2026-06-18

### Changed
- **Extras & Discounts are now coherent Pro features in the admin (upsell, not a dead editor).** Previously a free admin could open the Extras manager and the Discount Codes manager, create entries, and save them — but the booking layer ignored that data (premium pricing is stripped), causing "I added extras but customers can't select them" confusion. Now, on the free plan:
  - The standalone **Extras** page and the **Extras** + **Discount Codes** sections of the **Pricing** page render a clean "Upgrade to Pro" panel instead of an editor.
  - **Seasonal** and **Duration** pricing stay fully free (only the Discounts section of the Pricing page is gated).
  - The admin REST endpoints (`admin_get/create/update/delete_extra`, `…_discount`) now enforce the plan server-side: writes return an upgrade-required response, list endpoints return empty. This is product-coherence/defense-in-depth — the real revenue protection remains the stripped premium pricing code, not these gates.
- New reusable `templates/admin/partial-pro-upsell.php` for consistent upgrade panels.

## [1.0.50] – 2026-06-17

### Security
- **Server-authoritative reservation totals (price-tampering fix).** `create_reservation` previously trusted `total_price` from the request body, so a crafted POST could set any price (e.g. book a car for €1) — a vulnerability affecting even legitimate Pro sites. The total, discount and applied extras are now recomputed server-side via `BingoCR_Pricing::calculate()`; the client value is display-only.
- **Hardens the free/Pro split as a side effect.** Because `calculate()` applies extras/discounts/out-of-hours only when `BingoCR_Premium` exists (it is physically stripped from the free build), these can no longer be injected through a crafted request even if the `can()` gate were edited/bypassed. Verified: with the feature gate fully bypassed and premium absent, a request carrying extras + a discount code + out-of-hours times still yields the plain base total.

## [1.0.49] – 2026-06-17

### Added
- **PayPal webhook reconciliation.** New receiver at `POST /bingocr/v1/payments/paypal/webhook` handles `PAYMENT.CAPTURE.COMPLETED`. This is the safety net for the "captured at PayPal but the browser response was lost" case: PayPal notifies the site independently and the reservation is settled (marked paid, auto-confirmed, customer + admin emails sent) instead of being left stuck unpaid.
- **Two-layer authenticity.** When a Webhook ID is configured (Payment → PayPal), the request signature is verified via PayPal's `verify-webhook-signature` API; additionally — always — the capture is re-fetched from PayPal's API and must report `COMPLETED` before anything is settled. The request body is never trusted on its own. Transient lookup failures return HTTP 500 so PayPal retries; forged/irrelevant events are acknowledged (200) and dropped.
- New **Webhook ID** setting and the **Webhook URL** shown on the Payment page for easy PayPal setup. New DB helper `BingoCR_DB::get_payment_by_capture()`.

### Changed
- Extracted the "mark paid + confirm + email" logic into `BingoCR_PayPal::settle_reservation_paid()` (idempotent) so the synchronous browser capture and the asynchronous webhook settle a reservation identically — whichever arrives first wins, the other is a no-op.

## [1.0.48] – 2026-06-16

### Security
- **PayPal client secret encrypted at rest.** Previously the secret sat in plain text in `wp_options` (`bingocr_settings`), so a database leak would expose live PayPal credentials. Added `BingoCR_Crypto` (AES-256-GCM authenticated encryption) with the key derived from `wp_salt('secure_auth')` — which lives in `wp-config.php`, **not** the database — so a DB-only dump cannot decrypt it. The PayPal client (`BingoCR_PayPal`) decrypts on use; encryption is transparent.
- **Closed two browser-exposure leaks.** The full settings array (including the plain-text secret) was being dumped into a JS global via `wp_localize_script`, and the admin payment form echoed the secret into a password field's `value`. Both now redact the secret. The field is **write-only**: it renders empty with a "secret saved" placeholder, an empty submission keeps the stored value, and a new value is encrypted before storage. The settings POST response is redacted too.
- **Backward compatible.** Existing plain-text secrets pass through `decrypt()` unchanged and are upgraded to encrypted the next time the secret is saved. Graceful fallback if OpenSSL/GCM is unavailable on the host.

## [1.0.47] – 2026-06-16

### Fixed
- **Premium code now actually stripped from the free build.** The downloaded Freemius free build still contained the premium-only files (`class-bingocr-paypal.php`, `class-bingocr-premium.php`) — the `@fs_premium_only` docblock tag alone did **not** strip them, even though Freemius did flip `is_premium` to `false`. Renamed both files with Freemius's reliable `__premium_only` filename suffix (`class-bingocr-paypal__premium_only.php`, `class-bingocr-premium__premium_only.php`) and updated the conditional loader. Freemius strips `__premium_only` files by name, so the PayPal and premium pricing logic are now physically absent from the free build. The `@fs_premium_only` docblock and `class_exists()` runtime guards remain as belt-and-suspenders.

## [1.0.46] – 2026-06-16

### Changed
- **Freemius-compatible uninstall.** Removed `uninstall.php` — Freemius forbids it, because the file would run before the uninstall event is reported and block Freemius's uninstall tracking/feedback. The same cleanup (drops all plugin tables and deletes plugin options) now lives in `BingoCR_Activator::uninstall()` and is hooked to Freemius's `after_uninstall` action.

## [1.0.45] – 2026-06-16

### Fixed
- **Plugin Check pass.** Set `Tested up to` to **7.0** (the current WordPress; the earlier 6.7 was below current and flagged). Removed hidden build files (`.distignore`, `.vscode`, etc.) from the distribution archive — Plugin Check rejects any hidden files. Moved the PayPal SDK `phpcs:ignore` onto the `wp_enqueue_script` line so the intentional "no version" is correctly suppressed.
- Result: the only remaining Plugin Check notices are inside the bundled Freemius SDK (third-party, not subject to plugin-author fixes).

## [1.0.44] – 2026-06-15

### Changed
- **WordPress.org review prep.** Documented **PayPal** and **Freemius** as external services in the readme (alongside ipapi.co), each with what data is sent, when, and Terms/Privacy links.
- `uninstall.php` now also drops the `bingocr_payments` table and removes the legacy `bingocr_license_active` option, so a full uninstall leaves nothing behind.
- Corrected the readme `Tested up to` header to a released WordPress version.
- Added a `.distignore` so `wp dist-archive` builds also exclude `dev/` and `must`.

## [1.0.43] – 2026-06-15

### Security
- **Removed the two license overrides from `is_pro()`** — the `BINGOCR_DEV_PRO` constant and the `bingocr_license_active` option. These were dev/legacy shortcuts that would have let anyone unlock Pro by editing wp-config or a single DB row. Plan status is now determined **solely by Freemius** (`can_use_premium_code()`), with no constant override and no option fallback. Pro is tested with a Freemius sandbox license in dev mode.

## [1.0.42] – 2026-06-15

### Changed
- **Premium code is now physically stripped from the free build**, not just runtime-gated. The PayPal integration (`class-bingocr-paypal.php`) and a new `class-bingocr-premium.php` holding extras pricing, discount calculation and the out-of-hours fee are tagged `@fs_premium_only`, so the Freemius deployment removes them from the free ZIP.
- The pricing calculator and reservation endpoint call this logic behind `class_exists( 'BingoCR_Premium' )` / `class_exists( 'BingoCR_PayPal' )` guards, with the runtime `can()` check as a second layer. In the free build (premium classes absent) extras, discounts and the out-of-hours fee compute to zero and PayPal is unavailable — verified by tests.
- Premium files load via a conditional loader (`file_exists`) so the free build never fatals on the missing files.
- Removed the unused `csv_export` switchboard key.

### Notes
- `dark_theme`, `custom_color` and `offline_payments` stay runtime-gated — they have no separable server code to strip; they're covered by the license gate plus per-site activation and (optional) premium-build obfuscation.

## [1.0.41] – 2026-06-15

### Fixed
- **Freemius menu-slug collision** — the Pricing Rules admin page used the slug `bingocr-pricing`, which is the slug Freemius auto-generates for its own pricing/checkout page once paid plans are enabled. The two clashed, so "Upgrade Now" never reached the Freemius checkout. The Pricing Rules page is now `bingocr-pricing-rules`, freeing `bingocr-pricing` for Freemius.

## [1.0.40] – 2026-06-14

### Fixed
- **PayPal buttons disappeared at checkout** — a regression from 1.0.36, where a `?ver=` query argument was appended to the PayPal JS SDK URL (to satisfy a Plugin Check warning). PayPal's SDK rejects unknown query parameters, so it failed to load and `window.paypal` was never defined. The SDK URL no longer carries a version (with a documented `phpcs:ignore` for the resulting Plugin Check notice).

## [1.0.39] – 2026-06-14

### Fixed
- **Custom Primary Color (Pro) had no effect** — the inline style set `--bingocr-primary` while the stylesheet reads `--bcr-primary`, so the chosen color was ignored and the widget always rendered the default red. The two now match, so the brand color applies.

## [1.0.38] – 2026-06-14

### Changed
- **Attribution is now strictly opt-in for everyone** (default off) and is no longer a Pro feature. The "Powered by" link is never forced on the free plan — it renders only when the site owner deliberately enables it, in line with WordPress.org guidelines. Removed the `hide_attribution` switchboard key.

## [1.0.37] – 2026-06-14

### Changed
- **Consistent Pro badges across the admin** — dark theme, custom brand color and hide-attribution (Settings → Display) and the Extras / Discount Codes sections (Pricing) now show a "Pro" badge and disable their controls on the free plan, matching the out-of-hours and payments sections. These features were already enforced on the frontend; this makes the admin reflect it so nothing silently "doesn't take effect."

## [1.0.36] – 2026-06-14

### Fixed
- **Plugin Check (WordPress.org) warnings** — removed the discouraged `load_plugin_textdomain()` call (WordPress loads `.org` language packs and bundled `/languages` files automatically via just-in-time loading, since the `Domain Path` header is set), and added a version argument to the PayPal SDK `wp_enqueue_script()` call.

## [1.0.35] – 2026-06-14

### Changed
- **Offline and online payment methods are now gated independently** in the license switchboard via two keys — `offline_payments` (cash / card / bank selection) and `online_payments` (PayPal). Any combination is a one-word edit: offline free + PayPal Pro (the shipping default), everything Pro, or everything free.
- The Payment admin tab shows a Pro badge and disables the offline-methods section when `offline_payments` is gated; the booking widget and reservation endpoint honor the same gate.

## [1.0.34] – 2026-06-14

### Added
- **Payment Details in Reservations → View** — for online payments the popup now shows method, amount paid, PayPal fee and net received, transaction (capture) ID, order ID, payer email and the paid-on date, for tracking and traceability. Fee/net are parsed from PayPal's stored capture response.

### Security
- The payment-details endpoint is admin-only and returns only display-ready fields — the raw gateway response is never exposed to the browser.

## [1.0.33] – 2026-06-14

### Added
- **Live PayPal online payments** (Pro) — PayPal appears as a checkout method when enabled and configured. Flow: the booking creates a pending reservation → a PayPal order is created server-side → the customer approves in the PayPal popup → the server captures the payment.
- On a successful capture the reservation is marked **Paid** (`payment_status`, `paid_amount`) and **auto-confirmed**, and the confirmation + admin emails are sent (deferred from booking time for online payments).
- New `bingocr_payments` table recording each PayPal order/capture; `payment_status` and `paid_amount` columns on reservations (DB version 1.2, applied automatically on upgrade).
- A "Paid" badge on the admin reservations list.

### Security
- PayPal create-order and capture endpoints are nonce-protected and gated by the `online_payments` Pro feature; capture re-validates the order against PayPal and is idempotent (no double charge/confirm).

## [1.0.32] – 2026-06-14

### Added
- **Payment admin tab** — choose accepted pay-on-site methods (cash, card on pickup, bank transfer) and write payment instructions.
- **Payment method selection** in the booking modal — customers pick an enabled method; the choice is stored on the reservation and shown (translated) in the customer and admin emails, with the instructions in the customer email.
- **PayPal settings** (Pro) — enable, sandbox/live mode, client ID and secret. Settings save and are ready; the live online order/capture flow lands in a later phase.

### Security
- The server re-validates the submitted payment method against the methods the admin has enabled (can't be spoofed by the client).

## [1.0.31] – 2026-06-14

### Changed
- Seasonal adjustment detail now reads in **days** (e.g. "Summer (9 days, +10%)") to match the rest of the car-rental UI.

## [1.0.30] – 2026-06-13

### Added
- Seasonal adjustment is now **itemized per season** with the season name, day count and percentage (e.g. "Seasonal adjustment · Summer (9 days, +10%)") in the booking summary and emails. Rentals spanning multiple seasons show one line each.

## [1.0.29] – 2026-06-13

### Fixed
- **Seasonal pricing now always applies day-by-day** and is no longer hidden when a duration rule is active. The two compose: the duration rule sets the nightly rate and seasonal adjusts it per night. Seasonal adjustment is itemized in the booking summary and emails. (A duration "fixed total" is an explicit whole-rental price, so seasonal does not apply to it.)

## [1.0.28] – 2026-06-12

### Added
- **Admin interface is now fully translatable** — all JavaScript-built UI strings externalized through WordPress i18n.
- Translation template (POT) and complete Romanian translation (`ro_RO`) bundled in `/languages`.
- Each admin user sees the plugin in their own WordPress profile language.

## [1.0.27] – 2026-06-12

### Added
- **Filter bar on search results** — brand/model search, category and transmission filters, and price sorting.
- Single-car results are now centered on desktop.

## [1.0.26] – 2026-06-12

### Fixed
- Extras items were unreadable in the dark theme (theme `label` colors overrode the widget text color).

## [1.0.25] – 2026-06-12

### Fixed
- Language auto-detection now switches the **entire** booking widget UI, not just the reservation/email language; non-Romanian visitors get English even when the site default is Romanian.

## [1.0.24] – 2026-06-12

### Fixed
- Filtering reservations by status in the admin returned no results — ambiguous `status` column in the cars join, now qualified.

## [1.0.23] – 2026-06-12

### Added
- **Editable Terms & Conditions** in Settings; the "terms and conditions" text in the booking form is now a link that opens them in a popup (with a plain-text fallback when no terms are set).

## [1.0.22] – 2026-06-12

### Added
- **Business Hours & Out-of-Hours Fee** (Pro) — pickups/returns outside the configured hours add a fee per occurrence, shown live in the booking summary and included in emails.

## [1.0.21] – 2026-06-12

### Added
- **Pickup/return time selection** in the booking form (defaults 10:00), stored on the reservation and shown everywhere.
- **Order summary in emails** — base rental, each extra, discount, total and deposit in both customer and admin emails, plus pickup/return times and drop-off location.
- **Language-aware emails** — customer emails follow the language the booking was made in (EN/RO, self-contained strings, no .po files); admin notifications follow the site widget language.
- **Status-change emails** — customers are notified when a reservation is set to confirmed (green, with order summary) or cancelled (gray); sent only when the status actually changes.
- Local dev test harness `dev/test-modal.html` (excluded from distribution).

### Changed
- **Unified feature switchboard** — `BingoCR_License` now holds a single `$features` map (`'free'` / `'pro'` per feature) and a `$limits` map (per-plan numeric limits, `-1` = unlimited). Any feature or limit can be flipped between plans by editing one value in one file.
- `BingoCR_License::is_pro()` uses the cross-build-safe `can_use_premium_code()` Freemius check and supports a `BINGOCR_DEV_PRO` constant for local development.
- Free-plan limit error messages now reflect the configured limits instead of hardcoded numbers.
- Discount validation during reservation creation is now plan-gated (previously ungated).
- Redesigned booking modal layout: Order Summary is now a fixed strip below the header (always visible), with extras, discount code and customer form in the scrollable area.
- Extras use custom-drawn checkboxes immune to theme styling.
- Modernized admin design across all plugin pages (CSS-only design system).

### Fixed
- Extras card crushed to invisible in the booking modal (flex-shrink with overflow:hidden).
- Booking submit crash: Confirm button lives outside the form element.
- Confirm button stuck on "Loading…" after a successful booking.
- Duration pricing tier save error: header row was collected as a data row.
- One failing startup API call no longer blanks out locations/categories/extras.

---

## [1.0.3] – 2026-06-10

### Fixed
- Booking widget crash on pages with a category filter: the `/cars/categories` REST endpoint returns objects `{category, count}`, now normalized to plain strings before rendering.

### Added
- **Redesigned public booking widget** — improved search form with pickup date, return date, pickup location, drop-off location (with "Same as pickup" toggle) and category filter.
- **Booking modal** — opens when the user clicks "Book Now"; contains an extras cart, live price breakdown (base rental, duration rule, seasonal adjustment, extras, discount, deposit), discount code field and customer details form.
- **Default deposit** — new field in *Settings → Deposit*; used when a car's deposit type is set to "Default".
- **Per-car deposit** — deposit type (Default / Percentage / Flat) and value fields in the car edit form.
- **Duration Pricing Tiers** — day-range price tiers per car (e.g. days 1–5 at one rate, days 6–14 at a lower rate) in the car edit form; stored as period-based pricing rules scoped to the specific car.
- **Extras & Discounts on Pricing Rules page** — Extras/Add-ons and Discount Codes management sections added to the Pricing Rules admin page (reuses existing JS logic).

---

## [1.0.2] – 2026-05-01

### Changed
- Renamed all internal prefixes: `bingowp_` → `bingocr_`, `BingoWP_` → `BingoCR_`, `BINGOWP_` → `BINGOCR_`.
- Renamed all plugin files to `bingocr-*` for consistency.
- Shortcode changed from `[bingowp_rental]` to `[bingocr_rental]`.
- REST API namespace changed from `bingowp/v1` to `bingocr/v1`.
- Text domain changed from `bingowp` to `bingocar`.

---

## [1.0.1] – 2026-04-15

### Fixed
- Renamed plugin to comply with WordPress.org trademark guidelines (removed "WP" from name).
- Fixed database coding standards: added `phpcs:ignore` annotations on all direct `$wpdb` queries.
- Fixed variable naming in template files (added `bingocr_` prefix).
- Removed `load_plugin_textdomain()` call (deprecated since WP 4.6).
- Fixed missing translators comment in REST API.

### Changed
- Updated "Tested up to" to WordPress 7.0.

### Added
- `languages/` directory scaffold.

---

## [1.0.0] – 2026-03-01

### Added
- Initial release.
- Fleet management with WP Media Library photo picker.
- Online booking widget via `[bingocr_rental]` shortcode.
- Reservation dashboard (view, confirm, update, cancel).
- Locations management (pickup / drop-off with address and hours).
- Extras / add-ons per booking.
- Discount codes (percentage or flat, with usage limits and expiry).
- Automatic confirmation emails to customers and admins.
- Built-in EN / RO translations.
- Period-based and seasonal pricing rules.
