Slotara supports online payment collection at the time of booking, as well as offline payment recording by staff. Payment is intentionally separated from Slotara's SaaS subscription billing โ each tenant uses their own gateway credentials.
Client confirms booking
โโโบ Booking created (payment_status = pending)
โโโบ Amount > 0 AND tenant requires payment?
YES โ redirect to gateway checkout
NO โ instant confirmation (emails + SMS dispatched)
Payment gateway webhook
โโโบ BookingPaymentService::markPaid()
โโโบ payment_status = paid, paid_at = now()
โโโบ BookingNotificationService::dispatchForNewBooking()
โโโบ SyncBookingToGoogleCalendar (queued, if enabled)
Client redirected to success page
| Gateway | Best for |
|---|---|
| Stripe | Global businesses, all currencies |
| Razorpay | INR payments, South Asia |
| PayPal | Global PayPal users |
| Paddle | Digital goods, SaaS-style billing |
| Paystack | Nigeria, Ghana, South Africa & pan-African payments |
{success.fa-money} When Require payment at booking is on and a gateway is configured, the booking wizard's confirmation button reads Pay now (with the order total) instead of Confirm booking, so customers know a payment step is coming.
storage/logs/laravel.log| Gateway | Webhook URL |
|---|---|
| Stripe | https://yourdomain.com/stripe/webhook |
| Razorpay | https://yourdomain.com/razorpay/webhook |
| PayPal | https://yourdomain.com/paypal/webhook |
| Paddle | https://yourdomain.com/paddle/webhook |
| Paystack | https://yourdomain.com/paystack/webhook |
{warning} Every booking webhook is verified with this business's signing secret (Stripe
whsec_โฆ, Razorpay webhook secret, Paddle notification secret; PayPal events are confirmed with the PayPal API). Events that are unsigned, forged or for another business's booking are rejected, so paste the secret or bookings won't be marked paid by webhook. The payment success page also asks the gateway whether the payment really completed. Gateways can't reachlocalhost,.testor private IP addresses โ the Payments page warns you when your site runs on one.
{primary.fa-info-circle} Unpaid checkouts don't hold slots. A booking whose checkout was started but not paid is cancelled after 30 minutes (set
BOOKING_PAYMENT_HOLD_MINUTESin.env), freeing the slot; leaving the gateway's page frees it immediately. This needs the usual cron job (bookings:release-unpaidruns every five minutes). If a payment still arrives later, the booking is put back when the slot is free; otherwise the payment is kept, the booking stays cancelled and the business is emailed to rebook or refund. Prices are charged exactly: a price below a gateway's minimum charge (e.g. Stripe 0.50) can't be paid online.
{primary.fa-info-circle} Paddle has no hosted checkout page: Slotara opens Paddle's checkout on its own page (
/booking/payment/{token}/paddle) with Paddle.js. Enter the API key and the Client-side token (Paddle โ Developer tools โ Authentication), and add your domain under Paddle โ Checkout โ Website approval. Sandbox keys (pdl_sdbx_โฆ/test_โฆ) use Paddle's sandbox automatically.
{primary.fa-info-circle} Paystack signs webhooks with an HMAC-SHA512 signature derived from your Secret Key, so no separate webhook secret is needed โ the same Secret Key used for API calls also verifies incoming events. On the customer's return from Paystack, the transaction is additionally verified server-side by reference before the booking is marked paid.
Staff with booking management access can record payments manually:
payment_status is set to paid and notifications are dispatched if the booking was previously pendingOffline methods (Cash, Card terminal, Bank transfer) can each be toggled on or off in the Offline Payment Methods section of the payment settings page.
Every booking has a public receipt URL โ no login required:
GET /booking/{cancellation_token}/receipt
The page shows: business logo, name, and contact info; client name, email, and phone; appointment date/time and provider; service line items with total; payment status badge, gateway, reference number, and paid-at timestamp. A Print button is visible on screen but hidden when printing.
From the admin panel: Bookings โ View โ Print receipt opens the receipt in a new tab.
| Booking payments | SaaS subscription billing | |
|---|---|---|
| Stripe keys | Tenant-configured (TenantPaymentSettings) |
Platform-configured (BillingService) |
| Webhook route | /stripe/webhook (metadata type: booking) |
/billing/stripe/webhook |
| Purpose | Client pays for service | Tenant subscribes to Slotara |
These two flows never share credentials or webhook handlers.
| File | Purpose |
|---|---|
app/Support/TenantPaymentSettings.php |
Settings accessor |
app/Services/BookingPaymentService.php |
Core payment logic, requiresPayment(), markPaid() |
app/Services/BookingNotificationService.php |
Dispatches emails/SMS after payment |
app/Contracts/BookingPaymentGateway.php |
Gateway interface |
app/Services/PaymentGateways/ |
Stripe, Razorpay, PayPal, Paddle, Paystack implementations |
app/Http/Controllers/PaystackWebhookController.php |
Paystack webhook (verifies HMAC per tenant / platform) |
app/Http/Controllers/BookingPaymentController.php |
Return/cancel URL handlers (verifies Paystack return) |
app/Http/Controllers/BookingReceiptController.php |
Receipt page |
resources/views/booking/receipt.blade.php |
Receipt view |
tests/Feature/BookingPaymentTest.php |
Feature test suite |
tests/Feature/OfflinePaymentTest.php |
Offline payment tests |