7 review agents (pipeline run, self-review, codebase-context, frontend-placement, backend testing-gaps, Square-API, docs-parity) audited the SCA-primary work. ALL findings fixed, including every pre-existing red CI job: GDPR (HIGH): - anonymize_user() now scrubs admin_audit_log.target_user_id (mirrors delete_guest_user) so 2fa_fallback_charge rows (customer id + card_last4 PII) no longer survive registered-user account deletion; gdpr test added BACKEND TEST GAPS (all 10): - delivery-unavailable 503 branch: prod-tag predicate test + dev-variant marker - twoFactorFallbackEnabled alias/case/default matrix tests + exported wrapper - insertTwoFAFallbackAudit details-JSON shape + audit-row assertions for all 6 gate sites (booking/tip/gift-card/payment-method/terminal/till, both actors) - CreateTerminalPayment.VerificationToken: passthrough, too-long 400, 2FA-skip, token-less fallback + SCA-required (new terminal_sca_test.go) - isVerificationRequiredError at all 5 charge sites (402 + code:verification_required) - customer_initiated handler-level assertions (MIT false admin / CIT true customer) - Mock: ApprovePendingVerification, ChallengeResult auto/deny, _deny token suffix, parseVerifyToken unit tests FRONTEND SCA + Square-API (CRITICAL): - tokenizeSavedCardWithVerification reads result.token (the verified token) not result.verificationResult (deprecated verifyBuyer shape — saved-card SCA could never succeed in production before); parseTokenizeVerificationResult pure fn extracted + pinned in square.test.ts; 'verified' with no token proceeds tokenless - HIGH: saved-card idempotency key regenerated after a definitive 402 (fresh token under the same key = IDEMPOTENCY_KEY_REUSED dead-loop); kept on 503/cancelled - challenge-cancelled copy no longer promises a 2FA fallback the UI doesn't show; 'waiting for approval in your banking app' state on CIT surfaces - sca-unavailable demotion resets per attempt; card selection disabled mid-challenge; genuine saved-card declines no longer relabeled 'requires verification'; modal-close guard during processing; retry affordance standardized PIPELINE (every red job now green): - prod-tag build break fixed (shared square stub + test_helpers_test.go, prod-safe) - govulncheck: x/image 0.45.0 bumped (x/text resolved); go mod tidy clean - race: TestDeleteAccount_InvalidatesSquareCustomerCache made deterministic - DAV_ADMIN_PASSWORD placeholder in .env.example (compose config passes) - frontend: prettier 28 files, eslint, a11y 38 errors, knip (currentZIndex), deps in-range, audit vulns (nanoid/postcss) — all fixed; 67 vitest cases DOCS PARITY (6 DRIFTs + 5 GAPs): payments doc Ch4/Ch14/Appendix A, Technical Manual 2FA + counter-reset + payment sections, README test counts + SNAPSHOT_ENC_KEY, Feature Catalog, .env.example REQUIRE_2FA — SCA-primary/2FA-backup posture verified against code everywhere Verified: 26/26 dev + 24/24 prod packages, both vet tags, golangci-lint/staticcheck/ gosec 0 on both tags, gitleaks clean, 2,464 backend + 67 frontend tests.
344 lines
15 KiB
Svelte
344 lines
15 KiB
Svelte
<script lang="ts">
|
||
import { page } from '$app/stores';
|
||
import { resolve } from '$app/paths';
|
||
import { onMount } from 'svelte';
|
||
|
||
let format = $state('html');
|
||
|
||
let pdfNotice = $state(true);
|
||
|
||
onMount(() => {
|
||
format = $page.url.searchParams.get('format') || 'html';
|
||
if (format === 'pdf') {
|
||
// Strip ?format=pdf from the URL so a refresh doesn't re-trigger the print dialog.
|
||
const clean = window.location.pathname + window.location.hash;
|
||
history.replaceState(null, '', clean);
|
||
|
||
// Open the print dialog once the page is rendered.
|
||
// The notice element is removed before print so it won't appear in the PDF.
|
||
setTimeout(() => {
|
||
pdfNotice = false;
|
||
// Small delay so Svelte can remove the element before the print engine snapshots.
|
||
setTimeout(() => window.print(), 50);
|
||
}, 100);
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<svelte:head>
|
||
<title>Booking, Deposit & Cancellation Policy</title>
|
||
<style>
|
||
@media print {
|
||
:global(nav),
|
||
:global(.no-print) {
|
||
display: none !important;
|
||
}
|
||
:global(body) {
|
||
padding-top: 0 !important;
|
||
}
|
||
}
|
||
</style>
|
||
</svelte:head>
|
||
|
||
<div class="mx-auto max-w-2xl px-4 py-8 text-gray-900">
|
||
<h1 class="mb-2 border-b border-gray-200 pb-4 text-2xl font-bold">
|
||
Booking, Deposit & Cancellation Policy
|
||
</h1>
|
||
<p class="mb-8 font-mono text-xs text-gray-500">Last updated: 5 August 2026</p>
|
||
|
||
{#if format === 'pdf' && pdfNotice}
|
||
<p class="mb-6 rounded border border-gray-200 bg-gray-50 p-3 text-xs text-gray-600 italic">
|
||
Generating PDF… If the print dialog does not appear, use Ctrl+P / Cmd+P.
|
||
</p>
|
||
{/if}
|
||
|
||
<div class="space-y-8 text-sm leading-relaxed text-gray-700">
|
||
<!-- Section 1 -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">
|
||
1. Deposit Requirements & Booking Rules
|
||
</h2>
|
||
<p class="mb-3">
|
||
Where a deposit is required to secure your appointment, you must pay at least
|
||
<strong>20%</strong> of the total service cost before the 24-hour deadline prior to the
|
||
appointment. The 20% can be paid in a single payment or accumulated across multiple payments
|
||
— what matters is the total when the deadline passes. When deposit restrictions are active
|
||
on your account, appointments must be scheduled at least
|
||
<strong>36 hours in advance</strong>.
|
||
</p>
|
||
<p class="mb-3">
|
||
Payments toward your booking are capped at 100% of the total booking value based on service
|
||
prices at time of booking. Once you have paid the full amount, any additional payments above
|
||
100% of the booking value will be processed as tips (see Section 8 below).
|
||
</p>
|
||
<p class="mb-3">
|
||
To maintain fairness and prevent scheduling abuse, accounts with outstanding deposit
|
||
requirements are limited to <strong>one (1) active booking</strong> at any given time. No additional
|
||
appointments can be scheduled until your current appointment is either completed, cancelled, or
|
||
officially lapsed.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 2 -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">
|
||
2. Unpaid Deposits & The "Pending Release" Window
|
||
</h2>
|
||
<p class="mb-3">
|
||
If a required deposit is not paid at least 24 hours before the appointment begins, the
|
||
booking is shifted into a <strong>"Pending Release"</strong> status. The slot becomes vulnerable
|
||
— if another customer books an overlapping time and pays, your original booking is automatically
|
||
evicted.
|
||
</p>
|
||
<p class="mb-3">
|
||
While in this status, your appointment is <strong>not guaranteed</strong>. The system will
|
||
make this time slot visible to other clients. If another user attempts to book an
|
||
overlapping time and completes their deposit payment first, your unpaid booking will be
|
||
immediately and automatically evicted.
|
||
</p>
|
||
<p class="mb-3">
|
||
If the slot has not yet been claimed by another client, paying your outstanding deposit will
|
||
instantly restore your booking to a fully confirmed status.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 3 -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">3. Cancellation & Refund Tiers</h2>
|
||
<h3 class="mt-6 mb-2 text-sm font-semibold text-gray-800">Refund before service</h3>
|
||
<p class="mb-3">
|
||
We understand that plans can change. Eligibility for a refund before your service depends
|
||
strictly on the amount of notice provided prior to your scheduled appointment time. These
|
||
thresholds represent a genuine pre-estimate of the operational costs and loss of business
|
||
incurred by late cancellations. Refunds apply to <strong>booking payments only</strong>, up
|
||
to 100% of the total booking value.
|
||
</p>
|
||
|
||
<div class="mt-4 divide-y divide-gray-200 rounded-md border border-gray-200">
|
||
<div class="bg-gray-50/50 p-4">
|
||
<p class="font-semibold text-gray-900">Notice of more than 72 hours</p>
|
||
<p class="mt-1 text-xs text-gray-600">
|
||
You are entitled to a full 100% refund of all booking payments made.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="p-4">
|
||
<p class="font-semibold text-gray-900">Notice between 24 and 72 hours</p>
|
||
<p class="mt-1 text-xs text-gray-600">
|
||
Any booking payments made up to 50% of the total booking value are treated as a
|
||
Protected Deposit. This Protected Deposit is retained to cover the short-notice vacancy,
|
||
while any balance paid above 50% will be fully refunded.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="bg-gray-50/50 p-4">
|
||
<p class="font-semibold text-gray-900">Notice of less than 24 hours</p>
|
||
<p class="mt-1 text-xs text-gray-600">
|
||
All booking payments and deposits are entirely non-refundable and will be retained. The
|
||
cancellation will be logged as a missed appointment history strike.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 class="mt-6 mb-2 text-sm font-semibold text-gray-800">Refund after service</h3>
|
||
|
||
<p class="mb-3 text-sm leading-relaxed text-gray-700">
|
||
Refunds after booked appoinments have been carried out are at the salon owners discretion
|
||
based on the booking and reason, to arrange a refund please <a
|
||
href={resolve('/contact')}
|
||
class="font-medium text-blue-600 underline hover:text-blue-800">contact</a
|
||
> us to discuss a fair refund up to 100% of the value of the booking. Any paid tips will not be
|
||
considered as part of the refund as they are processed differently.
|
||
</p>
|
||
|
||
<h3 class="mt-6 mb-2 text-sm font-semibold text-gray-800">Refund Payment Method</h3>
|
||
<p class="mb-3 text-sm leading-relaxed text-gray-700">
|
||
Refunds are returned to the original payment method where possible:
|
||
</p>
|
||
<ul class="mb-4 list-disc space-y-2 pl-5 text-sm leading-relaxed text-gray-700">
|
||
<li>
|
||
<strong>Card payments</strong> (debit/credit card processed online or in-person): Refunded directly
|
||
back to the original card via Square. Processing times vary by card issuer (typically 3–10 working
|
||
days).
|
||
</li>
|
||
<li>
|
||
<strong>Gift card payments</strong>: Refunded back to the original gift card (or, if you
|
||
paid from your account balance, back to that balance). The gift card's remaining balance
|
||
is incremented and is immediately available for use. Expired gift cards are
|
||
non-refundable.
|
||
</li>
|
||
<li>
|
||
<strong>Cash payments</strong>: Credited to your account balance, available for immediate
|
||
use against future bookings or services.
|
||
</li>
|
||
</ul>
|
||
<p class="mb-4 text-xs leading-relaxed text-gray-500 italic">
|
||
If a card refund cannot be processed (e.g. the card is expired or the Square payment
|
||
reference is unavailable), we will notify you via your account and arrange collection of the
|
||
refund in person at the salon — please allow at least a day's notice so we can have cash on
|
||
hand. You will never be left out of pocket.
|
||
</p>
|
||
|
||
<h3 class="mt-6 mb-2 text-sm font-semibold text-gray-800">Guest / Walk-In Bookings</h3>
|
||
<p class="mb-3 text-sm leading-relaxed text-gray-700">
|
||
Walk-in customers who book without creating an account are issued a <strong
|
||
>guest account</strong
|
||
> to hold their booking and payment records. The same cancellation and refund tiers above apply
|
||
to guest bookings. Refunds are returned as follows:
|
||
</p>
|
||
<ul class="mb-4 list-disc space-y-2 pl-5 text-sm leading-relaxed text-gray-700">
|
||
<li>
|
||
<strong>Card payments</strong>: Refunded back to the original card via Square (same as
|
||
registered users).
|
||
</li>
|
||
<li>
|
||
<strong>Gift card payments</strong>: Refunded back to the original gift card. The gift
|
||
card's remaining balance is incremented. Expired gift cards are non-refundable.
|
||
</li>
|
||
<li>
|
||
<strong>Cash payments</strong>: Refunded in person at the salon. Please bring your receipt
|
||
and an admin will process your cash refund at the till.
|
||
</li>
|
||
</ul>
|
||
<p class="mb-3 text-xs leading-relaxed text-gray-500 italic">
|
||
Guest accounts do not hold rolling balances. All refunds for walk-in bookings are returned
|
||
via the original payment method or processed manually by salon staff.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 4 -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">4. Missed Appointments (No-Shows)</h2>
|
||
<p class="mb-3">
|
||
Failing to attend a confirmed appointment without notifying us in advance constitutes a
|
||
"No-Show". Cancelling a pending or deposit-lapsed booking within 24 hours does
|
||
<strong>not</strong> count as a no-show — only confirmed bookings (where the slot was secured
|
||
with a payment) can incur no-show strikes.
|
||
</p>
|
||
<p class="mb-3">
|
||
If your account accumulates <strong
|
||
>two (2) No-Show strikes within a rolling 6-month period</strong
|
||
>, our system will automatically restrict your account privileges, making upfront deposits
|
||
mandatory for future booking attempts (3 deposits required before deposits are automatically
|
||
cleared). No-show strikes older than 6 months are automatically excluded from the count.
|
||
</p>
|
||
<p class="mb-3">
|
||
Each completed booking with a payment reduces the required deposit count by one. Once the
|
||
count reaches zero, all prior no-show records within the 6-month window are forgiven and
|
||
your account returns to normal — no upfront deposits required — until a new no-show occurs.
|
||
The salon can also forgive individual no-shows at management's discretion, which immediately
|
||
removes them from the count.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 5 -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">
|
||
5. Statutory Rights & Exceptional Circumstances
|
||
</h2>
|
||
<p class="mb-3">
|
||
This policy is strictly aligned with the <strong>Consumer Rights Act 2015</strong>. Nothing
|
||
in these terms limits your statutory right to receive services carried out with reasonable
|
||
care and skill, or your right to a full refund if we are forced to cancel your appointment
|
||
due to our own scheduling conflicts.
|
||
</p>
|
||
<p class="mb-3">
|
||
Please note that in accordance with UK statutory exclusions for distance contracts, the
|
||
standard 14-day statutory cancellation "cooling-off" period under the Consumer Contracts
|
||
Regulations 2013 does not apply to online bookings scheduled for a specific date or time.
|
||
</p>
|
||
<p class="mb-3">
|
||
That exclusion does not apply to gift cards: online gift-card purchases may be cancelled
|
||
within 14 days for a refund to the original payment method under the Consumer Contracts
|
||
Regulations 2013. If the card has been partly used, the amount already spent on salon
|
||
services is not refundable, and the remaining unspent balance is refunded to the original
|
||
payment method; the card is then cancelled. A card that has been redeemed to an account
|
||
balance or fully spent cannot be cancelled.
|
||
</p>
|
||
<p class="mb-3">
|
||
Where a partly-used card is cancelled, the card is cancelled automatically when the refund
|
||
is issued, so the remaining balance cannot then be spent. See our Gift Card Terms for the
|
||
full position.
|
||
</p>
|
||
<p class="mb-3">
|
||
If you believe your statutory consumer rights have not been met, you can get free, impartial
|
||
advice from
|
||
<a
|
||
href="https://consumeradvice.scot"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="font-medium text-blue-600 underline hover:text-blue-800">consumeradvice.scot</a
|
||
>
|
||
(advice.scot). If that does not resolve the issue, you can escalate your complaint to your local
|
||
Trading Standards office. Claims up to £5,000 can also be pursued through the Scottish courts'
|
||
Simple Procedure.
|
||
</p>
|
||
<p class="mb-3">
|
||
We recognize that genuine emergencies, sudden severe illness, or bereavement can occur. Our
|
||
management team retains complete administrative system access to waive cancellation fees,
|
||
refund deposits, or clear no-show history strikes on a case-by-case basis under exceptional
|
||
circumstances.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 6 -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">6. Policy Amendments and Updates</h2>
|
||
<p class="mb-3">
|
||
We reserve the right to amend, update, or modify this Booking, Deposit & Cancellation Policy
|
||
at any time to reflect changes in our business operations, system features, or legal
|
||
obligations under UK and Scottish law.
|
||
</p>
|
||
<p class="mb-3">
|
||
Any updates will be published directly to this page, and the "Last updated" date at the top
|
||
will change accordingly. For active bookings scheduled prior to an amendment, the terms in
|
||
place at the exact time your booking was created will apply. Continued use of our booking
|
||
system after a policy revision implies formal agreement to the updated terms.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 7 -->
|
||
<section class="border-t border-gray-200 pt-6">
|
||
<h2 class="mb-2 text-base font-semibold text-gray-900">7. Governing Law</h2>
|
||
<p class="mb-4">
|
||
These terms and conditions are governed by and construed in accordance with the laws of
|
||
Scotland. Any disputes or legal claims arising from these provisions shall be subject to the
|
||
exclusive jurisdiction of the Scottish courts.
|
||
</p>
|
||
<p class="text-xs text-gray-500">
|
||
If you need to request an adjustment due to exceptional circumstances or have questions
|
||
regarding your upcoming appointments, please use our official <a
|
||
href={resolve('/contact')}
|
||
class="font-medium text-blue-600 underline hover:text-blue-800">Contact Channels</a
|
||
> to get in touch.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Section 8: Tips -->
|
||
<section>
|
||
<h2 class="mb-3 text-base font-semibold text-gray-900">8. Tips</h2>
|
||
<p class="mb-3">
|
||
Tips are voluntary payments made in addition to the booking total. Tips can only be added
|
||
<strong>after your booking has started</strong> — they cannot be paid in advance.
|
||
</p>
|
||
<p class="mb-3">
|
||
Unlike booking payments, <strong>tips are not refundable</strong>. Tips are not considered
|
||
payment for the service itself, but rather a voluntary expression of satisfaction. If you
|
||
cancel your booking, any tips already paid will not be included in the cancellation refund.
|
||
</p>
|
||
<p class="mb-3">
|
||
Tips can be added via your account after the booking has started, or at the time of payment
|
||
when paying in person at the salon. When paying by card at the terminal, you will be
|
||
prompted to add a tip if you wish.
|
||
</p>
|
||
<p class="mb-3 text-xs text-gray-500 italic">
|
||
If you believe a tip was added in error, please contact us via our official
|
||
<a
|
||
href={resolve('/contact')}
|
||
class="font-medium text-blue-600 underline hover:text-blue-800">Contact Channels</a
|
||
> and we will review your case.
|
||
</p>
|
||
</section>
|
||
</div>
|
||
</div>
|