Skip to content

Releases: reachweb/statamic-resrv

v6.0.0-beta2

25 Apr 10:08

Choose a tag to compare

v6.0.0-beta2 Pre-release
Pre-release

This release continues the v6.0.0 line. It builds on beta1 with new payment-flow features (per-gateway surcharges and amount limits), a brand-new calendar implementation, additional dynamic pricing rules, and a number of correctness fixes around payment intents and customer creation.

Migration

This release includes one new migration that you need to run with php artisan migrate. A new payment_surcharge column is added to the resrv_reservations table to support per-gateway surcharges.

Calendar Replacement (Alpine Calendar)

The frontend datepicker has been completely rewritten on top of the new @reachweb/alpine-calendar package, replacing vanilla-calendar-pro. The new component is fully Alpine-native, removes the modal/popup boilerplate, and ships significantly less Blade markup.

Highlights:

  • Inline popup mode with built-in mobile handling (no manual isMobile switching).
  • Range mode honours minimum_reservation_period_in_days / maximum_reservation_period_in_days natively via minRange / maxRange.
  • Disabled days of the week and per-day price/availability metadata are now passed declaratively as props.
  • Fixed a regression in showAvailabilityOnCalendar where unavailable dates would still render a price label.
  • Fixed z-index of the popup so it no longer renders behind sticky headers.

New calendarRules property

AvailabilitySearch now accepts a calendarRules array that is forwarded to the underlying calendar component. You can use it to declaratively add per-date rules (disabled ranges, special markers, etc.) without touching the Blade template:

<livewire:availability-search :calendar-rules="[
    ['type' => 'disabled', 'from' => '2026-12-24', 'to' => '2026-12-26'],
]" />

Template Changes

If you have published availability-dates.blade.php you must replace it with the new version — the entire template was rewritten and the previous modal-based markup is no longer compatible. The calendar JS callbacks (onCreateDateEls, onClickDate, handleRanges, addPriceToEachDate, etc.) have been replaced by the new @calendar:change / @calendar:open Alpine events.

Per-Gateway Payment Surcharge

You can now charge a surcharge on top of the reservation amount based on which payment method the customer selects. This is useful for cards, PayPal, or any gateway whose acquirer fees you want to pass on.

Configure per-gateway in config/resrv-config.php:

'payment_gateways' => [
    'stripe' => [
        'class' => \Reach\StatamicResrv\Http\Payment\StripePaymentGateway::class,
        'label' => 'Credit Card',
    ],
    'paypal' => [
        'class' => \App\Payment\PayPalPaymentGateway::class,
        'label' => 'PayPal',
        'surcharge' => ['type' => 'percent', 'amount' => 4],
    ],
    'bank_transfer' => [
        'class' => \Reach\StatamicResrv\Http\Payment\OfflinePaymentGateway::class,
        'label' => 'Bank Transfer',
        'surcharge' => ['type' => 'fixed', 'amount' => 5],
    ],
],
  • type is percent or fixed.
  • The surcharge is shown in the gateway picker (+4% surcharge, +€ 5.00 surcharge).
  • The surcharge is rendered as its own line on the checkout payment table and in the "payable now" total.
  • The reservation's payment column stays as the booking amount (read by the CP, API, emails). The surcharge is persisted separately as payment_surcharge. The gateway is charged the full sum via the new Reservation::totalToCharge() helper.
  • The CP reservation page and the "made"/"confirmed" email templates now show Payment surcharge and Total charged rows whenever a surcharge is present.

Payment Method Amount Limits

You can now restrict a payment method to a price range. For example, hide "Credit Card" for orders over €1000, or only offer "Bank Transfer" on orders over €500.

'payment_gateways' => [
    'stripe' => [
        'class' => \Reach\StatamicResrv\Http\Payment\StripePaymentGateway::class,
        'amount_limits' => ['min' => 10, 'max' => 1000],
    ],
    'bank_transfer' => [
        'class' => \Reach\StatamicResrv\Http\Payment\OfflinePaymentGateway::class,
        'amount_limits' => ['min' => 500],
    ],
],
  • Both min and max are inclusive and optional — omit either to leave that side unbounded.
  • A gateway that fails the check is hidden from the picker entirely.
  • If only one gateway clears the filter, it auto-selects without showing a picker.
  • If none clear the filter, the customer is bounced back to step 2 with noGatewayAvailableForAmount.
  • Misconfiguration (min > max, unknown keys, non-numeric values) throws InvalidArgumentException at boot so problems fail loudly instead of silently hiding gateways.
  • Surcharge is not included in the comparison — the comparison uses the reservation's base payment amount.

Stale Payment Intent Cancellation

Payment intents are now cancelled at the provider whenever the customer abandons one — going back from step 3, switching gateways at the picker, applying a coupon, or re-entering checkout with a stale intent still attached. Previously, abandoned intents were silently left behind on Stripe and could later succeed via 3DS / async confirmation, producing webhook events for amounts the reservation no longer reflected.

The webhook handler now also detects a "stale intent" (one whose payment_id no longer matches the reservation) and refuses to cascade-confirm or cascade-cancel it. Stale-success events are logged for manual reconciliation.

Breaking: New cancelPaymentIntent() method on PaymentInterface

If you maintain a custom payment gateway, you must implement:

public function cancelPaymentIntent(string $paymentId, Reservation $reservation): void;

For offline / fake gateways this can be a no-op. For provider-backed gateways (Stripe, PayPal, Mollie, etc.), see the new Step 9 in UPGRADE.md for a full implementation reference and edge cases.

Min / Max Dynamic Pricing Rules

Two new amount_operation values are now available on dynamic pricing rules:

  • minimum — clamp the resulting price to a floor.
  • maximum — clamp the resulting price to a ceiling.

Both are restricted to the fixed amount type (a percent floor/ceiling makes no sense). The CP panel was updated to enforce this and to switch the picker automatically when one of these operations is selected.

Customer Creation Guard

CheckoutForm no longer creates a new Customer row each time the customer re-submits the second step (e.g. after fixing a validation error). If a customer is already attached to the reservation, that row is updated in place — no more orphaned customer rows.

Coupon UI Improvements

  • The coupon input is now hidden once the customer reaches step 3 / has selected a payment gateway. Re-entering coupons at this stage was a frequent source of intent/amount desync.
  • Applying or removing a coupon while a gateway is already selected now correctly clears the persisted surcharge and re-applies it against the new base amount, and cancels any in-flight intent.

Bug Fixes & Improvements

  • Fixed availability calendar showing the wrong price when showAvailabilityOnCalendar is enabled and an unavailable property's price was being surfaced.
  • The reservation totals API now exposes paymentSurcharge so custom checkout templates can read it.
  • AvailabilityResults / Checkout references in HandlesPricing switched to imported classes (purely a tidy-up).
  • Multisite availability test stabilised after upstream Statamic 6 changes.
  • PHPUnit dependency bumped to ^12.0 || ^13.0. Orchestra Testbench requirement bumped to ^9.0 || ^10.0 (Testbench 8 dropped along with PHP 8.1).

Template Changes

If you have published the checkout payment table component (livewire/components/checkout-payment-table.blade.php), update it to render the new surcharge row and recompute "payable now" to include the surcharge. See the new version shipped with this release for the exact markup.

If you have published the gateway picker (livewire/components/checkout-gateway-picker.blade.php), update it to render the surcharge label next to each gateway's name.

If you have published the language file (resources/lang/vendor/statamic-resrv/en/frontend.php), add these new keys:

'surcharge' => 'surcharge',
'paymentSurcharge' => 'Payment surcharge',
'noGatewayAvailableForAmount' => 'No payment method is available for this amount.',
'gatewayNotAvailableForAmount' => 'The selected payment method is not available for this amount.',

If you have published the config file, add the new commented-out surcharge and amount_limits examples under payment_gateways.

If you have published the checkout payment Livewire view (livewire/checkout.blade.php), update the :amount prop passed to <livewire:checkout-payment> from $this->reservation->fresh()->payment->format() to $this->reservation->fresh()->totalToCharge() so Stripe receives the surcharged total, and gate the coupon component on $step < 3.

Custom Payment Gateways

If you maintain a custom payment gateway, see the updated UPGRADE-PAYMENT-GATEWAYS.md (Step 9) for the new cancelPaymentIntent() requirement and per-provider implementation notes.

v6.0.0-beta1

03 Apr 14:44

Choose a tag to compare

v6.0.0-beta1 Pre-release
Pre-release

This release contains various core changes that we will push before the final Statamic v6 version. Resrv 6 will be compatible with Statamic 6 only.

Migration

This release includes database migrations that you need to run using php artisan migrate. A new payment_gateway column is added to the reservations table, and an abandoned_email_sent_at column is added for tracking abandoned email delivery.

Multiple Payment Gateways

You can now offer multiple payment methods during checkout. Customers will see a payment method picker when more than one gateway is configured.

Configure it in config/resrv-config.php:

'payment_gateways' => [
    'stripe' => [
        'class' => \Reach\StatamicResrv\Http\Payment\StripePaymentGateway::class,
        'label' => 'Credit Card',
    ],
    'offline' => [
        'class' => \Reach\StatamicResrv\Http\Payment\OfflinePaymentGateway::class,
        'label' => 'Bank Transfer / Pay at Premises',
    ],
],

Each gateway now has its own webhook URL: /resrv/api/webhook/{gateway} (e.g., /resrv/api/webhook/stripe). Make sure to update your webhook URL in your payment provider's dashboard. The legacy /resrv/api/webhook URL continues to work for single-gateway setups.

If you don't configure payment_gateways, the existing payment_gateway config continues to work exactly as before.

If you are using a custom payment gateway it will need to be updated to work with this release.

Offline Payment Gateway

A new built-in payment gateway for bank transfer or pay-at-premises scenarios. When selected, customers can confirm their reservation without an online payment. The reservation is confirmed immediately, and the confirmation email is sent right away.

Abandoned Reservation Emails

Send recovery emails to customers who started but didn't complete their reservation. The feature is opt-in and runs via a scheduled artisan command:

php artisan resrv:send-abandoned-emails

New config options:

enable_abandoned_emails (default: false) — enable/disable the feature
abandoned_email_delay_days (default: 1) — days to wait after the reservation expires before sending
Add the command to your scheduler to run daily. One email per customer is sent, and duplicate sends are prevented automatically.

Reservation Email Overrides

Email settings have been completely reworked. You can now customize email behavior per event and per form, directly from the Control Panel under the Resrv config section.

Per-Entry / Per-Collection Checkout Forms

You can now assign different checkout forms to specific entries or collections. This replaces the old form_name config (which still works as a fallback).

New config options:

Performance Improvements

Dynamic pricing lookups are now significantly faster. Cached data is indexed by key instead of being filtered on every request, resulting in O(1) lookups instead of O(n) scans.

Bug Fixes

Fixed anonymous Blade components not loading unless they were published to the vendor folder. Override and package paths are now both registered, so you can override specific components while using defaults for the rest.

Template Changes

If you have published/overridden the checkout template, you need to update it. The step 3 (payment) section has changed to support the payment method picker:

Before:

@if ($step === 3)
    <livewire:checkout-payment :clientSecret="$clientSecret" :publicKey="$publicKey" :amount="$this->reservation->fresh()->payment->format()" />
@endif

After:

@if ($step === 3)
    @if (count($availableGateways) > 1 && empty($selectedGateway))
        <x-resrv::checkout-gateway-picker :gateways="$availableGateways" />
    @else
        <livewire:checkout-payment
            :clientSecret="$clientSecret"
            :publicKey="$publicKey"
            :amount="$this->reservation->fresh()->payment->format()"
            :paymentView="$paymentView"
        />
    @endif
@endif

If you have published the language file (resources/lang/vendor/statamic-resrv/en/frontend.php), add these new keys:

'selectPaymentMethod' => 'Select payment method',
'changePaymentMethod' => 'Change payment method',
'offlinePaymentDescription' => 'Complete your reservation by confirming below. Payment can be arranged separately.',
'offlinePaymentAmount' => 'Amount due',

Config file: Backup your config file and run php artisan vendor:publish --tag=resrv-config --force to get the updated config, or manually add the new options. The new sections are: payment_gateways, checkout_forms_, enable_abandoned_emails, abandoned_email_delay_days, and reservation_emails_.

Or even better check the config from the control panel, adjust and save.

v5.1.2

13 Feb 12:17

Choose a tag to compare

  • Fix for the getAvailabilityCalendar method that caused it not to work in SQLite.

v5.1.1

10 Feb 17:07

Choose a tag to compare

  • Improved the reservationFromUri in the Resrv tag to use a hash of the email and the reference code so that it can be used in a more secure manner.

v5.1.0

04 Feb 09:34
1cfc22f

Choose a tag to compare

New feature: Availability List

  • New availability-list component that allows you to display all available dates relative to the user the user has chosen. Documentation will be updated soon.
  • Postgres fixes: Many fixes to add support for postgres database. SQLite and MySQL / MariaDB are still recommended until further testing.
  • Livewire 4 support.

v5.0.0

05 Dec 08:04

Choose a tag to compare

This release contains breaking changes please read follow the release notes to upgrade existing Resrv installations.

New features

  • Totally rewritten Extras and Options functionality. Instead of using Blade components this functionality has now been moved into their own Livewire components. This minimizes use of JavaScript in the frontend and results in faster and more secure functionality.
  • Added a new "days of the week" setting in the Mass Availability modal that allows you to only apply the changes in specific days of the selected range.
  • Added a new availability-results-advanced Blade component that you can use to display all the possible advanced properties and their availability / pricing.
  • Added a new Cutoff field that allows you to set a specific start time and schedule for each entry and set a cutoff time in hours after which reservations are no longer possible.
  • Added more robust connected availabilities rules.
  • Changed the $resetAdvancedOnBoot property of AvailabilitySearch to $resetOnBoot and it now also resets the quantity and triggers a search.
  • Moved the customer data into a separate Customer model and database table. This will help us create account functionality in the future.
  • Moved the availability calendar into a popup in order to provide much better mobile support.
  • Removed the need for the Resrv Availability field to have the "resrv_availability" handle in the Statamic blueprints.
  • Added a "when extra is NOT selected" condition rule to show / hide / require an extra if another extra is NOT selected.
  • Added extra category conditions to show / hide / require extras based on categories.
  • Added support for wildcard coupons using * to apply coupons to all entries.
  • Added affiliate - coupon relationship to connect affiliates with coupons.
  • Added the ability to close the calendar popup with the Esc key.
  • Added support for multiple webhook secrets in the payment gateway configuration.
  • The payment gateway now passes the public key through for easier frontend configuration.
  • Email templates now use the configured logo.
  • Made the calendar input readonly to prevent manual text input.
  • Use override label in emails and frontend if set.

Bug fixes

  • Fixed some countries not appearing in the phone codes field of the checkout form.
  • Fixed some issues with reservations made on the current day for the same day.
  • Fixed an issue where coupons wouldn't update the total if applied after step 1.
  • Fixed affiliate removal when coupon is removed.
  • Fixed wildcard coupons on update event.
  • Fixed Resrv tag getting schedule for wrong entry.
  • Fixed reverse condition for extras.
  • Fixed wrong email getter.
  • Fixed refund emails being sent multiple times.
  • Various other fixes you can find in GitHub.

Upgrading

  • Make a backup of your database.
  • Migrate your database by running php artisan migrate
  • The resrv_customers table should now have your customer data. This should be handled automatically by the migration but please check your database afterwards.
  • There are major changes in Resrv's frontend views: if you were overriding those you need to manually upgrade the following views:
    • checkout.blade.php - needs to be upgraded to use the new Extras and Options components. Check the file in this release to check what needs to be changed.
    • availability-results.blade.php - also needs to be upgraded to use the new Extras and Options component - even if you had not enabled them here.
    • Also in availability-results.blade.php you need to add the section for displaying the cutoff feature error if you use it.
    • checkout-extras.blade.php and checkout-extra.blade.php - These have been completely replaced by extras.blade.php and extra.blade.php
    • checkout-options.blade.php and checkout-option.blade.php - These have been completely replaced by options.blade.php and option.blade.php
    • If you were using availability-extras.blade.php or availability-options.blade.php they also need to be upgraded.
    • checkout-payment-table.blade.php needs to be upgraded to use the new Extras and Options objects. Also check the changes in this release.
  • You also need to update your email templates (again if you were overriding those). We have tried to respect backwards compatibility here but changing the following in all email templates is best practice:
    • Any mention of $reservation->customer->get('email) should now be $reservation->customer->email.
    • Replace $reservation->customer->count() > 1 with $reservation->has('customer')
    • Replace @foreach ($reservation->customer as $field => $value) with @foreach ($reservation->customerData as $field => $value).

v4.1.0

18 Mar 09:02

Choose a tag to compare

  • Laravel 12 support
  • Added a fix for the new country code dictionary so that it can be used in regular Statamic forms and pass the value to the email template correctly.
  • Cleanup of unused database columns and tables.

v4.0.1

01 Mar 14:30

Choose a tag to compare

  • Fix issues with diffInDays method changing in Carbon v3.

v4.0.0

01 Mar 10:16

Choose a tag to compare

Changelog

  • New frontend calendar based on Vanilla Calendar Pro.
    • Added the capability to display price and availability per day inside the calendar itself.
    • The calendar now respects minimum and maximum durations and doesn't allow the user to select an unsupported range.
  • New extra categories feature: your extras can now belong to categories.
  • Extras are now using the Entry model to associate themselves with Entries.
  • Extras are no longer editable inside an Entry using the resrv_extras field.
  • Full conditional extras support (Show / hide or require extras based on conditions).
  • New phone dictionary (both frontend and backend) for the checkout form that displays country phone code select box plus a text input for the phone number.
  • Added a resrv:import-entries command that can be used to sync the site's Entries to Resrv's database.
  • Improved extension's config display in the control panel and various panels.
  • Upgraded the Stripe library to the latest version.
  • Various small bug fixes.

Upgrading

  • Make a backup of your database.
  • Migrate your database by running php artisan migrate
  • The resrv_statamicentry_extra table is being replaced by the resrv_entry_extra table. This should be handled automatically by the migration but please check your database afterwards.
  • There are major changes in Resrv's frontend views: if you were overriding those you need to manually upgrade the availability-search.blade.php, checkout-extras.blade.php, checkout-extra.blade.php files (and possibly more like the dictionary fields).

v3.2.0

01 Oct 14:09

Choose a tag to compare

  • Removed all the legacy API endpoints, controllers and more.
  • Added more tests to cover scenarios that were tested from the API endpoints.
  • Fix an issue that when a date range would contain the date that Daylight saving starts, the duration would be a day less.