The overview shows stats for today, the week, or the month. It should not be trusted to show an accurate revenue. For accurate information refer to your Stripe dashboard.
All confirmed bookings will automatically be visible here. You can view bookings by the day, week, or month. Bookings that have been confirmed and paid for will be green. Bookings that are locked, and awaiting payment from the user will be gray.
A booking can be clicked on to view its details. Here you can alter the player count and holes. Alternatively you can cancel the booking entirely.
In an any empty slot you can manually add a booking. Manually added bookings do not support online payment. You can search for existing customers in here by name, email, or phone. Or you can create a booking without associating a user. Finally, you can set a custom price for the booking or use the default prices.
When a customer who booked online arrives you can click on their booking and view the payment status. Under the price one of the following statuses will be relayed:
| Status | Meaning |
|---|---|
| Payment Fulfilled | The payment was successfully processed. The customer is good to go. |
| Payment Due at Course | This booking was made manually and payment was never processed online. The customer still needs to pay. |
| Payment Pending | The payment is still processing, it is likely to go through. |
| Payment Declined* | The payment was declined. The customer still needs to pay. |
| Payment Canceled* | The payment was canceled by Stripe. The customer still needs to pay. |
| Payment Requires Action* | The payment was not completed. The customer still needs to pay. |
While viewing a booking, you can update the status to "arrived" or "no show", which will be reflected in the Stats page.
Cancelations can be made by updating the status to "canceled". You can cancel a booking as long as it is before the tee time. When a booking is canceled a refund will automatically be issued in accordance with the refund policy.
Customers are also able to cancel their tee time themselves in their user dashboard.
The refund policy is clearly stated on the payment page when making a booking:
"Full refund if cancelled more than 24 hours before your tee time. Cancellations within 24 hours of your tee time are subject to a $20 cancellation fee, which will be deducted from your refund. Once your tee time has started this purchase is non-refundable."
The refund is automatically issued when a user cancels their booking online, or an administrator cancels the booking in the admin dashboard. If a refunded tee time is cheaper than the cancelation fee, then up to the value of the tee time will be refunded.
You can set the time of the first tee time and the last tee time here. Regardless of slot intervals, tee times will always be within these two times. The first tee time will always be available, but based on the spacing between tee times the last tee time may not be available.
You can also set which days of the week bookings will be available here.
Users can be searched by email, phone, or name. Once a user has been found you can alter their name, email, or phone. And furthermore, you can delete the user entirely.
Here you can set the default price for 9 holes and 18 holes. Both must have a price. This price can be overriden with a different price with a slot interval (see Slot Intervals below).
Here you can set the default booking system features. Maximum days ahead is the number of days in the future a user can book.
Tee-Time Spacing is the number of minutes that tee times will be spaced by default. This value can be overridden with a slot interval (see below).
The Lock Time is the number of seconds that a user is granted in order to reserve a booking. This is necessary to prevent a user from indefinitely holding a tee time. It also guarantees that once a user has initiated the process of booking a tee time, then no other user can book the same time while it is locked for the first user.
A slot interval defines how tee times should be spaced and their price during a specific time period of the day, as well as on specific days of the week.
A slot interval is uniquely defined by its start and end, and the days that it is active on. A slot interval's start time can be the same as a different slot interval's end time. Otherwise, there must be no overlap when any two intervals are active on the same day.
When creating a slot interval, you'll be notified if it is invalid.
If there is no slot interval defined for a time period and day(s), then the system will use the default spacing and pricing.
For example, using slot intervals you could space tee times by 8 minutes from 8am to 10am, a tighter spacing since the course is empty. Once the course is full you can have another interval from 10am to 2pm that spaces tee times by 12 minutes. Increasing the spacing to give each group more time to get through the first hole, and so on. Ideally, reducing the chance of golfers having to wait on the group in front of them.
As long as there is no overlap between slot intervals, you can set the spacing and price during this time period. This will allow you to set different pacing and pricing on weekends among other things.
Tip: You can even block off a period of time completely on one or more days by settings the spacing interval to be more minutes than the time from start to end.
User accounts are created through the login interface by selecting the "Register" tab. Registration validates input data through the Auth class before creating accounts.
Valid registration requires:
The registration process validates email uniqueness through database queries and creates password hashes using PHP's password_hash() function. Upon successful registration, users are automatically logged in and redirected to their intended destination.
Admin accounts require direct database access for creation and cannot be created through the standard registration interface.
The system uses JWT tokens for stateless authentication. When users log in successfully, the server issues a signed token containing user identification and permissions that expires after one hour.
Token management includes:
The validateToken() method verifies token integrity and automatically refreshes tokens nearing expiration, ensuring seamless user experience during extended sessions.
The APIMiddleware class handles token validation for protected endpoints. It intercepts requests, validates tokens, and manages authorization redirects when credentials are invalid or expired.
Key middleware features:
When users attempt to access protected content with expired tokens, the system automatically redirects to the login page while preserving their intended destination. The client-side apiFetch() function stores the original request in session storage and includes the source page in request headers.
After successful login, users are redirected back to their original page, and any stored requests are automatically retried, providing seamless reauthorization without data loss.
The API is divided into public and restricted endpoints based on authentication requirements:
Public endpoints (no authentication required):
/api/v1/tee-times/ - Available tee times and pricing information/api/v1/contact/ - Contact form submissions/api/v1/auth/ - User login and registrationRestricted endpoints (authentication required):
/api/v1/booking/ - Tee time reservations and payment processing/api/v1/user/ - Account management and booking history/api/v1/admin/ - Administrative functions (requires admin privileges)The middleware automatically enforces these restrictions and handles unauthorized access attempts by triggering the reauthorization flow.
The reservation system manages the complete lifecycle of tee time bookings through a multi-step process that ensures data integrity and prevents double-booking through temporal locks.
TeeTimeController handles the generation and validation of available time slots based on configurable intervals and existing reservations. The controller uses a binary search algorithm to efficiently place new slots while maintaining minimum spacing requirements between bookings.
BookingController manages the reservation lifecycle from initial lock through payment processing. It integrates with Stripe for payment handling and implements automatic cleanup through MySQL scheduled events.
TeeTimeManager (client-side) provides the user interface state management and coordinates between time selection, locking, and payment confirmation steps.
Step 1: Time Selection
Available times are calculated in real-time by the findAvailable() method, which considers existing reservations, slot intervals, and course operating hours. The algorithm uses binary search to efficiently identify valid time slots that maintain proper spacing.
Step 2: Slot Locking
When a user selects a time, newLock() creates a temporary reservation with status 'locked'. This prevents other users from booking the same slot while maintaining exclusivity for a configurable duration (default managed by lock_hold_seconds). A MySQL scheduled event automatically abandons the lock if payment isn't completed within the hold period.
Step 3: Payment Processing
The locked reservation upgrades to 'requires_confirmation' status during payment processing. Stripe handles the actual payment through confirmation tokens, with the system supporting various payment states including additional authentication requirements.
Step 4: Confirmation
Successful payments transition the reservation to 'booked' status, triggering confirmation emails and real-time updates to other users via Server-Sent Events.
The system uses Server-Sent Events (SSE) through PubSubController to broadcast reservation state changes. When slots are locked, booked, or released, all connected clients receive immediate updates without requiring page refreshes.
Pricing is determined through a combination of default rates and time-specific slot intervals. The determinePrice() method checks for custom pricing during specific time periods and days, falling back to default course pricing when no intervals match.
Multiple validation layers ensure booking integrity:
The system uses custom exceptions (BookingException, PaymentException, RefundException) to provide specific error handling and user feedback.