Overview
The booking and payment flow involves several models and services working together:Step 1: Check availability
GET /api/availabilites → AvailabilityService::getAvailableTimes()
File: app/Models/Services/v1/AvailabilityService.php
The availability service:
- Resolves the tutor’s
Teachrecord for the subject - Gets the tutor’s weekly
DayAvailabilityschedule - Generates 15-minute interval time slots for the requested dates
- Filters out:
- Times that clash with existing lessons
- Times blocked by calendar events (Google Calendar sync)
- Times affected by travel time between lessons (via BlackBox)
- Handles timezone conversion (UTC offset from request)
- Returns
AvailableDateTimecollections with pricing viaLessonPriceBreakdown
Lesson duration constraints
Fromconfig/app.php:
- Minimum: 15 minutes
- Maximum: 120 minutes
- Default: 60 minutes
Travel time logic
File:app/Models/v1/AvailabilityRequestTime.php
For in-person lessons, the system calculates travel time between lesson locations and creates buffer zones around existing lessons. Uses the BlackBox microservice for transit time estimates.
Step 2: Create booking
POST /api/bookings → BookingController
Creates:
- A
Lessonrecord with start/finish times, subject, level, tutor - A
Bookingrecord linking the lesson to the student - A
LessonAddressif it’s an in-person lesson - Optionally links children via
lesson_childpivot
AWAITING_PAYMENT (3)
Step 3: Make payment
POST /api/payments/pay → PaymentController → StripeService::createCharge()
File: app/Models/Services/Payments/StripeService.php
Create PaymentIntent
Creates a Stripe PaymentIntent with manual capture (
capture_method: manual). This pre-authorizes the amount without charging immediately.Handle 3D Secure
If Stripe returns
requires_action, the response includes a redirectURL for the client to complete 3D Secure authentication. The client handles this and calls back.Handle requires_capture
If payment is
requires_capture, the booking state updates to REQUIRES_CAPTURE (5). The payment is authorized but not yet charged.Create receipt
StripeService::createReceipt() creates a PaymentReceipt with:charge_id— Stripe charge referenceamount— total chargedapplication_fee_idandapplication_fee_amountcommission_id— links to active commission for fee calculationcoupon_id— if a discount code was used
Payment status checking
GET /api/payments/{id}/status → returns current PaymentIntent status:
| Status | Constant | Meaning |
|---|---|---|
processing | STATUS_PROCESSING | Payment being processed |
succeeded | STATUS_SUCCEEDED | Payment completed |
canceled | STATUS_CANCELLED | Payment cancelled |
requires_action | STATUS_REQUIRES_ACTION | 3D Secure needed |
requires_capture | STATUS_REQUIRES_CAPTURE | Pre-authorized, awaiting capture |
Step 4: Payout to tutor
Payouts are handled by theSendPendingPayouts scheduled job (runs every 15 minutes).
File: app/Jobs/SendPendingPayouts.php
Find eligible bookings
Queries bookings that:
- Have state
PAYED (1) - Have a
PaymentReceiptwith notransfer_idyet - Are 2+ days old (settlement period)
Create Stripe transfer
StripeService::createPayout() creates a Stripe Connect transfer:- Destination: tutor’s Stripe Connect account (
acct_*) - Amount: lesson price minus platform fees
- Uses either
transfer_grouporsource_transactionmethod
Commission calculation
File:app/Models/v1/Commission.php
The active commission record defines fee percentages:
| Fee | Field | Description |
|---|---|---|
| Commission fee | commission_fee | Platform commission on lesson price |
| Service fee | service_fee | Fee charged to student |
| Payout fee | payout_fee | Fee on tutor payout |
| Payout amount fee | payout_amount_fee | Fixed payout fee |
| Management fee | account_management_amount_fee | Monthly account management (first lesson of month) |
PaymentReceipt model auto-calculates the full fee breakdown in its boot() method using CommissionService.
Refunds
StripeService::refund() supports both full and partial refunds:
- Full refund →
REFUNDED (0) - Partial refund →
PARTIAL_REFUNDED (4)
Coupon system
File:app/Models/Coupon.php
| Type | Constant | Description |
|---|---|---|
| One-time use | ONE_TIME_USE | Can be used once per user |
| Two-time use | TWO_TIME_USE | Can be used twice per user |
| Discount type | Constant | Description |
|---|---|---|
| Amount | DISCOUNT_TYPE_AMOUNT | Fixed amount off |
| Percentage | DISCOUNT_TYPE_PERCENT | Percentage off (with optional max_discount_amount cap) |