Bỏ qua

Functional Requirements - Football Field Booking System

Version: 1.0
Last Updated: January 2025
Total Requirements: 30


Requirements Priority Levels

  • P0 (Critical): Must-have for MVP, blocks launch
  • P1 (High): Important for MVP, but can workaround
  • P2 (Medium): Nice-to-have for MVP, can defer
  • P3 (Low): Future enhancement

FR-1: User Management

FR-1.1: User Registration

ID: FR-1.1
Priority: P0 (Critical)
User Story: As a new user, I want to register an account so that I can book fields.

Acceptance Criteria:

  • [ ] User can register with email and password
  • [ ] User can register via Google OAuth
  • [ ] User can register via Facebook OAuth
  • [ ] Email verification is required before first login
  • [ ] Password must meet complexity requirements:
  • Minimum 8 characters
  • At least 1 uppercase letter
  • At least 1 lowercase letter
  • At least 1 number
  • At least 1 special character
  • [ ] User receives welcome email after successful registration
  • [ ] Duplicate email addresses are rejected with clear error message
  • [ ] User profile is created in User Service
  • [ ] User data is synced to Keycloak

Business Rules:

  • Default role: Customer
  • Email must be verified within 24 hours or account is deleted
  • Username must be unique (email is username)

Dependencies: Keycloak integration, Email service
API Endpoints: POST /auth/register
Estimated Effort: 5 story points


FR-1.2: User Login

ID: FR-1.2
Priority: P0 (Critical)
User Story: As a registered user, I want to login so that I can access my account.

Acceptance Criteria:

  • [ ] User can login with email/password
  • [ ] User can login via Google OAuth
  • [ ] User can login via Facebook OAuth
  • [ ] JWT access token (15 min expiry) and refresh token (7 days) issued
  • [ ] Invalid credentials return 401 with message "Invalid email or password"
  • [ ] Account lockout after 5 failed attempts within 15 minutes
  • [ ] Locked account can be unlocked after 30 minutes or password reset
  • [ ] User can remember login (refresh token stored securely)
  • [ ] Login attempt is logged (audit trail)

Business Rules:

  • Unverified email cannot login
  • Locked accounts cannot login until unlocked
  • Only one active session per user (optional: multi-device)

Dependencies: Keycloak integration
API Endpoints: POST /auth/login
Estimated Effort: 5 story points


FR-1.3: User Profile Management

ID: FR-1.3
Priority: P1 (High)
User Story: As a user, I want to manage my profile information.

Acceptance Criteria:

  • [ ] User can view profile information (name, email, phone, avatar, preferences)
  • [ ] User can update name (2-50 characters)
  • [ ] User can update phone (valid phone format with country code)
  • [ ] User can upload avatar (max 5MB, jpg/png only)
  • [ ] Avatar is resized to 400x400px automatically
  • [ ] User can change password (requires current password)
  • [ ] User can update notification preferences (email, SMS)
  • [ ] Changes are validated before saving
  • [ ] User receives confirmation after successful update
  • [ ] Profile cache is invalidated after update

Business Rules:

  • Email cannot be changed (requires new verification flow)
  • Profile updates are reflected immediately
  • Old avatars are deleted to save storage

API Endpoints:

  • GET /users/me
  • PUT /users/me
  • PUT /users/me/password
  • POST /users/me/avatar

Estimated Effort: 3 story points


FR-1.4: User Roles & Permissions

ID: FR-1.4
Priority: P0 (Critical)
User Story: As a system, I need to distinguish between different user types and control access.

Roles:

Role Permissions How to Get
Customer - Book fields
- View own bookings
- Cancel own bookings
- Update own profile
Default on registration
Field Owner - All Customer permissions
- Add/edit own fields
- View bookings for own fields
- Manage field availability
Apply + Admin approval
Admin - All permissions
- Manage all users
- Manage all fields
- Manage all bookings
- View analytics
Assigned by system admin

Acceptance Criteria:

  • [ ] User is assigned Customer role by default on registration
  • [ ] Field Owner role requires approval workflow
  • [ ] Admin role can only be assigned by existing admin
  • [ ] Roles determine accessible API endpoints (RBAC enforced)
  • [ ] Unauthorized access returns 403 Forbidden
  • [ ] Role changes are logged for audit

Business Rules:

  • User can have multiple roles
  • Customer → Field Owner requires admin approval
  • Field Owner status can be revoked by admin

Dependencies: Keycloak roles configuration
Estimated Effort: 5 story points


FR-1.5: Password Reset

ID: FR-1.5
Priority: P1 (High)
User Story: As a user, I want to reset my password if I forget it.

Acceptance Criteria:

  • [ ] User can request password reset via email
  • [ ] Reset link is sent to user's registered email
  • [ ] Reset link contains secure token (JWT)
  • [ ] Reset link expires after 1 hour
  • [ ] User can set new password via reset link
  • [ ] New password must meet complexity requirements
  • [ ] Old password is invalidated immediately
  • [ ] All active sessions are terminated
  • [ ] User receives confirmation email after password change
  • [ ] Password reset attempts are rate-limited (3 per hour)

Business Rules:

  • Token is single-use only
  • Password cannot be same as last 3 passwords
  • Email must be verified to reset password

API Endpoints:

  • POST /auth/forgot-password
  • POST /auth/reset-password

Estimated Effort: 3 story points


FR-1.6: User Account Deletion

ID: FR-1.6
Priority: P2 (Medium)
User Story: As a user, I want to delete my account and all my data.

Acceptance Criteria:

  • [ ] User can request account deletion from profile settings
  • [ ] User must confirm deletion (enter password)
  • [ ] Active/upcoming bookings prevent deletion
  • [ ] User data is soft-deleted (marked as deleted, not physically removed)
  • [ ] User receives confirmation email
  • [ ] Account can be restored within 30 days by contacting support
  • [ ] After 30 days, data is permanently deleted (GDPR compliance)
  • [ ] User's reviews/ratings are anonymized (Phase 2)

Business Rules:

  • Cannot delete if active bookings exist
  • Can delete if only completed bookings exist
  • Refunds must be processed before deletion

API Endpoints: DELETE /users/me
Estimated Effort: 3 story points


FR-2: Field Management

FR-2.1: Field Registration

ID: FR-2.1
Priority: P0 (Critical)
User Story: As a field owner, I want to register my field on the platform.

Acceptance Criteria:

  • [ ] Owner can create field listing via form
  • [ ] Required fields: name, address, location (lat/lng), sport type, price per hour
  • [ ] Optional fields: description, amenities, rules, operating hours
  • [ ] Support multiple images upload (max 10, 5MB each, jpg/png)
  • [ ] Images are automatically compressed and resized
  • [ ] Address auto-complete with Google Maps API
  • [ ] Location (lat/lng) is auto-populated from address
  • [ ] Field requires admin approval before going live
  • [ ] Owner receives email when field is approved/rejected
  • [ ] Draft fields can be saved and edited before submission

Business Rules:

  • Sport types: Football 5v5, 7v7, 11v11
  • Price range: 50,000 - 1,000,000 VND per hour
  • Operating hours: Must be reasonable (e.g., 6 AM - 11 PM)
  • Admin approval required for all new fields

API Endpoints: POST /fields
Estimated Effort: 8 story points


FR-2.2: Field Information Management

ID: FR-2.2
Priority: P1 (High)
User Story: As a field owner, I want to update my field information.

Acceptance Criteria:

  • [ ] Owner can edit all field details (name, description, price, etc.)
  • [ ] Owner can add new images (up to 10 total)
  • [ ] Owner can delete existing images
  • [ ] Owner can reorder images (first image is cover photo)
  • [ ] Owner can set different prices for different time periods (e.g., peak hours)
  • [ ] Owner can set availability hours per day of week
  • [ ] Owner can temporarily disable field (mark as unavailable)
  • [ ] Changes are reflected immediately after save
  • [ ] Field cache is invalidated after updates
  • [ ] Owner receives confirmation after successful update

Business Rules:

  • Cannot delete all images (minimum 1 required)
  • Price changes do not affect existing bookings
  • Disabling field cancels pending bookings (with refund)

API Endpoints:

  • PUT /fields/:id
  • POST /fields/:id/images
  • DELETE /fields/:id/images/:imageId
  • PUT /fields/:id/availability

Estimated Effort: 5 story points


ID: FR-2.3
Priority: P0 (Critical)
User Story: As a customer, I want to search for available fields near me.

Acceptance Criteria:

  • [ ] Search by location (radius in km)
  • [ ] Filter by sport type (5v5, 7v7, 11v11)
  • [ ] Filter by price range (min, max)
  • [ ] Filter by available date and time
  • [ ] Filter by amenities (parking, shower, changing room, lighting)
  • [ ] Results are sorted by distance (nearest first) by default
  • [ ] Alternative sorting: price (low to high, high to low), rating (Phase 2)
  • [ ] Pagination support (20 items per page)
  • [ ] Show distance from user's location
  • [ ] Show starting price for each field
  • [ ] Search performance < 200ms (p95)
  • [ ] Results are cached for 5 minutes

Business Rules:

  • Only approved and active fields are shown
  • Only fields with availability for selected time are shown
  • Default radius: 5 km

API Endpoints: GET /fields/search

Query Parameters:

?lat=10.762622
&lng=106.660172
&radius=5000
&sportType=football_7
&minPrice=50000
&maxPrice=200000
&date=2024-01-20
&startTime=10:00
&endTime=12:00
&amenities=parking,shower
&sortBy=distance
&page=1
&limit=20

Estimated Effort: 8 story points


FR-2.4: Field Availability

ID: FR-2.4
Priority: P0 (Critical)
User Story: As a customer, I want to see real-time field availability for a specific date.

Acceptance Criteria:

  • [ ] Display available time slots for selected date
  • [ ] Show booked slots (grayed out, unavailable)
  • [ ] Show price for each time slot
  • [ ] Support different prices for different times (peak/off-peak)
  • [ ] Minimum booking duration: 1 hour
  • [ ] Maximum booking duration: 8 hours
  • [ ] Time slots are in 30-minute increments
  • [ ] Availability updates in real-time (cache invalidation)
  • [ ] Consider field operating hours
  • [ ] Consider booking buffer time (15 min between bookings)
  • [ ] Cache availability data (Redis, 1 min TTL)
  • [ ] Invalidate cache when booking is created/cancelled

Business Rules:

  • Availability = Operating hours - Booked slots
  • Buffer time prevents back-to-back bookings
  • Expired pending bookings are considered available

API Endpoints: GET /fields/:id/availability?date=2024-01-20

Response Example:

{
  "fieldId": "uuid",
  "date": "2024-01-20",
  "slots": [
    {
      "startTime": "10:00",
      "endTime": "11:00",
      "available": true,
      "price": 150000
    },
    {
      "startTime": "11:00",
      "endTime": "12:00",
      "available": false,
      "bookingId": "uuid"
    }
  ]
}

Estimated Effort: 8 story points


FR-2.5: Field Details

ID: FR-2.5
Priority: P1 (High)
User Story: As a customer, I want to view detailed information about a field.

Acceptance Criteria:

  • [ ] Display all field information (name, description, price, amenities)
  • [ ] Show image gallery with lightbox
  • [ ] Display location on interactive map (Google Maps)
  • [ ] Show exact address and directions link
  • [ ] Display amenities with icons
  • [ ] Show field rules and policies
  • [ ] Display operating hours
  • [ ] Show contact information (optional)
  • [ ] Display reviews and average rating (Phase 2)
  • [ ] Show "Similar Fields Nearby" section (3 fields)
  • [ ] Include "Book Now" button
  • [ ] Share field via social media (Phase 2)

Business Rules:

  • Only approved fields are publicly accessible
  • Disabled fields show "Currently Unavailable" message

API Endpoints: GET /fields/:id
Estimated Effort: 5 story points


FR-3: Booking Management

FR-3.1: Create Booking

ID: FR-3.1
Priority: P0 (Critical)
User Story: As a customer, I want to book a field for a specific time.

Acceptance Criteria:

  • [ ] Customer selects field, date, start time, end time
  • [ ] System validates field availability in real-time
  • [ ] System acquires distributed lock (Redis) before creating booking
  • [ ] System checks for overlapping bookings with pessimistic lock
  • [ ] If no conflicts, create booking with status PENDING
  • [ ] Calculate total price (hours × price per hour)
  • [ ] Release distributed lock after booking creation
  • [ ] User is redirected to payment page
  • [ ] Booking expires if not paid within 15 minutes
  • [ ] Expired bookings are automatically cancelled
  • [ ] Email confirmation sent after payment (not immediately)

Business Rules:

  • Minimum booking duration: 1 hour
  • Maximum booking duration: 8 hours
  • Booking must be at least 1 hour in advance
  • No overlapping bookings allowed (enforced by lock + DB constraint)
  • Price is locked at time of booking (won't change if field updates price)

API Endpoints: POST /bookings

Request:

{
  "fieldId": "uuid",
  "startTime": "2024-01-20T10:00:00Z",
  "endTime": "2024-01-20T12:00:00Z",
  "notes": "Birthday party, need extra time for setup"
}

Response:

{
  "bookingId": "uuid",
  "status": "PENDING",
  "totalPrice": 300000,
  "expiresAt": "2024-01-20T09:15:00Z",
  "paymentUrl": "https://payment-gateway.com/checkout/xyz"
}

Estimated Effort: 13 story points


FR-3.2: View Bookings

ID: FR-3.2
Priority: P1 (High)
User Story: As a user, I want to view my booking history.

Acceptance Criteria:

  • [ ] Customer can view own bookings only
  • [ ] Field owner can view bookings for their fields only
  • [ ] Admin can view all bookings
  • [ ] Filter by status (pending, confirmed, cancelled, completed, expired)
  • [ ] Filter by date range (start date, end date)
  • [ ] Sort by booking date (newest first, oldest first)
  • [ ] Sort by field name (alphabetical)
  • [ ] Pagination support (20 items per page)
  • [ ] Show booking summary (field name, date/time, price, status)
  • [ ] Click to view booking details

Business Rules:

  • Customers see only their bookings
  • Field owners see bookings for fields they own
  • Admin sees all bookings

API Endpoints:

  • GET /bookings/me (customer)
  • GET /bookings/fields/:fieldId (owner)
  • GET /bookings (admin)

Query Parameters:

?status=confirmed
&startDate=2024-01-01
&endDate=2024-01-31
&sortBy=bookingDate
&sortOrder=desc
&page=1
&limit=20

Estimated Effort: 5 story points


FR-3.3: Cancel Booking

ID: FR-3.3
Priority: P1 (High)
User Story: As a customer, I want to cancel my booking if my plans change.

Acceptance Criteria:

  • [ ] Customer can cancel confirmed booking only
  • [ ] Cancellation allowed up to 24 hours before start time
  • [ ] System calculates refund amount based on policy
  • [ ] Booking status changed to CANCELLED
  • [ ] Field availability is updated (slot becomes available)
  • [ ] Refund is automatically processed via Payment Service
  • [ ] Customer receives cancellation confirmation email
  • [ ] Field owner receives notification of cancellation
  • [ ] Cancel button is disabled if within 24h of start time
  • [ ] Provide cancellation reason (optional)

Refund Policy:

  • More than 48 hours before: 100% refund
  • 24-48 hours before: 50% refund
  • Less than 24 hours: No refund (no cancellation allowed)

Business Rules:

  • Cannot cancel completed bookings
  • Cannot cancel already cancelled bookings
  • Cannot cancel if start time has passed

API Endpoints: PUT /bookings/:id/cancel

Request:

{
  "reason": "Weather forecast shows heavy rain"
}

Estimated Effort: 8 story points


FR-3.4: Booking Conflict Detection

ID: FR-3.4
Priority: P0 (Critical)
User Story: As a system, I must prevent double bookings at all costs.

Acceptance Criteria:

  • [ ] Acquire Redis distributed lock before checking availability
  • [ ] Lock key format: booking:lock:${fieldId}:${date}
  • [ ] Lock timeout: 30 seconds
  • [ ] If lock acquisition fails, retry 3 times with exponential backoff
  • [ ] After acquiring lock, check for overlapping bookings in database
  • [ ] Use SQL query with FOR UPDATE (pessimistic lock)
  • [ ] Overlapping condition: (new_start < existing_end) AND (new_end > existing_start)
  • [ ] If overlap found, reject with 409 Conflict error
  • [ ] If no overlap, create booking within same transaction
  • [ ] Release lock after transaction completes (success or failure)
  • [ ] Handle lock timeout gracefully (return 503 Service Unavailable)
  • [ ] Log all lock operations for debugging

Technical Implementation:

async createBooking(data: CreateBookingDto) {
  const lockKey = `booking:lock:${data.fieldId}:${data.date}`;
  const lock = await this.redis.acquireLock(lockKey, 30000);

  try {
    // Check overlaps with pessimistic lock
    const overlap = await this.bookingRepo
      .createQueryBuilder()
      .where('fieldId = :fieldId', { fieldId: data.fieldId })
      .andWhere('status IN (:...statuses)', {
        statuses: ['PENDING', 'CONFIRMED']
      })
      .andWhere('(startTime < :endTime AND endTime > :startTime)', {
        startTime: data.startTime,
        endTime: data.endTime,
      })
      .setLock('pessimistic_write')
      .getOne();

    if (overlap) {
      throw new ConflictException('Time slot already booked');
    }

    // Create booking
    return await this.bookingRepo.save(data);
  } finally {
    await lock.release();
  }
}

Estimated Effort: 8 story points


FR-3.5: Booking Status Workflow

ID: FR-3.5
Priority: P0 (Critical)
User Story: As a system, I need to manage booking lifecycle states correctly.

Status Flow:

PENDING → CONFIRMED → COMPLETED
   ↓           ↓
EXPIRED    CANCELLED

Status Definitions:

  • PENDING: Booking created, awaiting payment (15 min timeout)
  • CONFIRMED: Payment successful, booking is confirmed
  • EXPIRED: Payment timeout, booking auto-cancelled
  • CANCELLED: User or admin cancelled booking
  • COMPLETED: Booking end time has passed

Transitions:

From To Trigger Event Emitted
PENDING CONFIRMED Payment success booking.confirmed
PENDING EXPIRED 15 min timeout booking.expired
CONFIRMED CANCELLED User cancels booking.cancelled
CONFIRMED COMPLETED Time passed booking.completed

Acceptance Criteria:

  • [ ] Only valid transitions are allowed (validated in code)
  • [ ] Invalid transitions return 400 Bad Request
  • [ ] Status changes trigger Kafka events
  • [ ] Events are consumed by Notification Service
  • [ ] Scheduled job runs every minute to:
  • [ ] Expire PENDING bookings after 15 min
  • [ ] Complete CONFIRMED bookings after end time
  • [ ] Status history is logged (audit trail)
  • [ ] Booking cannot be modified after CONFIRMED status

Estimated Effort: 5 story points


FR-3.6: Booking Modification

ID: FR-3.6
Priority: P2 (Medium)
User Story: As a customer, I want to modify my booking if I need to change the time.

Acceptance Criteria:

  • [ ] Customer can change booking time (date, start time, end time)
  • [ ] Customer can add/update notes
  • [ ] Modification allowed up to 24 hours before original start time
  • [ ] System checks availability for new time slot
  • [ ] System calculates price difference (can be positive, negative, or zero)
  • [ ] If price increased, customer must pay difference
  • [ ] If price decreased, refund is issued
  • [ ] Modification requires field owner approval (optional)
  • [ ] Customer receives confirmation email with updated details
  • [ ] Original booking is updated (not new booking created)

Business Rules:

  • Cannot modify within 24h of start time
  • Cannot change field (must cancel and create new booking)
  • Can modify unlimited times (before 24h cutoff)
  • Price is recalculated based on current field pricing

API Endpoints: PUT /bookings/:id

Request:

{
  "startTime": "2024-01-20T14:00:00Z",
  "endTime": "2024-01-20T16:00:00Z",
  "notes": "Updated: Need 2 hours instead of 1"
}

Estimated Effort: 8 story points


FR-3.7: Booking Reminders

ID: FR-3.7
Priority: P2 (Medium)
User Story: As a customer, I want to receive reminders so I don't forget my booking.

Acceptance Criteria:

  • [ ] Email reminder sent 24 hours before booking start
  • [ ] SMS reminder sent 2 hours before booking start (optional, costs money)
  • [ ] Reminders include:
  • [ ] Field name and address
  • [ ] Booking date and time
  • [ ] Directions link (Google Maps)
  • [ ] Contact information
  • [ ] Cancellation policy reminder
  • [ ] User can opt-out of reminders in profile settings
  • [ ] Scheduled job runs every hour to send due reminders
  • [ ] Reminders are only sent for CONFIRMED bookings
  • [ ] Reminder status is tracked (sent, failed)
  • [ ] Failed reminders are retried (max 3 attempts)

Business Rules:

  • No reminders for PENDING, CANCELLED, EXPIRED bookings
  • Opt-out applies to all future bookings
  • SMS reminders require verified phone number

Estimated Effort: 5 story points


FR-3.8: Booking Receipt

ID: FR-3.8
Priority: P1 (High)
User Story: As a customer, I want a receipt for my booking for record-keeping.

Acceptance Criteria:

  • [ ] Generate PDF receipt with professional layout
  • [ ] Include company logo and branding
  • [ ] Include booking details:
  • [ ] Booking ID
  • [ ] Field name and address
  • [ ] Date and time
  • [ ] Duration
  • [ ] Price breakdown
  • [ ] Include payment information:
  • [ ] Payment method
  • [ ] Transaction ID
  • [ ] Payment date
  • [ ] Amount paid
  • [ ] Include QR code for verification
  • [ ] Email receipt automatically after payment confirmation
  • [ ] Allow download from booking details page
  • [ ] Allow re-download anytime from booking history
  • [ ] Receipt is immutable (cannot be edited)

API Endpoints: GET /bookings/:id/receipt

Response: PDF file (application/pdf)

Estimated Effort: 5 story points


FR-4: Payment Processing

FR-4.1: Payment Integration

ID: FR-4.1
Priority: P0 (Critical)
User Story: As a customer, I want to pay for my booking securely online.

Payment Gateways:

  • Stripe: International credit/debit cards
  • VNPay: Vietnam domestic payment methods (bank transfer, e-wallets)

Acceptance Criteria:

  • [ ] Support credit/debit card payments (Visa, Mastercard, Amex)
  • [ ] Support bank transfer via VNPay
  • [ ] Support e-wallets (Momo, ZaloPay) via VNPay
  • [ ] Secure payment flow (redirect to gateway, return with result)
  • [ ] PCI-DSS compliant (never store card details)
  • [ ] Payment timeout after 15 minutes
  • [ ] User can retry payment if failed
  • [ ] Automatic retry for transient failures (network issues)
  • [ ] Payment gateway errors are handled gracefully
  • [ ] User receives clear error messages

Business Rules:

  • Payment is required to confirm booking
  • Payment amount matches booking total price exactly
  • Payment is non-refundable after cancellation cutoff
  • Partial payments not supported

API Endpoints:

  • POST /payments/create
  • POST /payments/confirm

Estimated Effort: 13 story points


FR-4.2: Payment Webhook Handling

ID: FR-4.2
Priority: P0 (Critical)
User Story: As a system, I need to receive and process payment confirmations from gateways.

Acceptance Criteria:

  • [ ] Receive webhooks from Stripe at /webhooks/stripe
  • [ ] Receive webhooks from VNPay at /webhooks/vnpay
  • [ ] Verify webhook signatures (Stripe: HMAC-SHA256, VNPay: MD5)
  • [ ] Reject webhooks with invalid signatures
  • [ ] Implement idempotency (prevent duplicate processing)
  • [ ] Use idempotency key (payment transaction ID)
  • [ ] On payment success:
  • [ ] Update booking status to CONFIRMED
  • [ ] Update payment status to COMPLETED
  • [ ] Emit payment.completed event (Kafka)
  • [ ] Invalidate field availability cache
  • [ ] On payment failure:
  • [ ] Keep booking status as PENDING
  • [ ] Update payment status to FAILED
  • [ ] Allow user to retry payment
  • [ ] Log all webhook events to MongoDB (audit trail)
  • [ ] Handle late/duplicate webhooks gracefully
  • [ ] Return 200 OK to gateway within 5 seconds

Security:

  • Validate webhook source IP (whitelist)
  • Verify signature before processing
  • Rate limit webhook endpoints

API Endpoints:

  • POST /webhooks/stripe
  • POST /webhooks/vnpay

Estimated Effort: 8 story points


FR-4.3: Payment Status Tracking

ID: FR-4.3
Priority: P1 (High)
User Story: As a user, I want to track the status of my payment in real-time.

Payment Statuses:

  • PENDING: Payment initiated, awaiting confirmation
  • PROCESSING: Payment is being processed by gateway
  • COMPLETED: Payment successful
  • FAILED: Payment failed
  • REFUNDED: Payment refunded (partial or full)

Acceptance Criteria:

  • [ ] User can view payment status on booking details page
  • [ ] Payment status updates in real-time (polling every 3 seconds)
  • [ ] Show payment method used
  • [ ] Show transaction ID
  • [ ] Show amount paid
  • [ ] Show payment date/time
  • [ ] Show refund amount (if applicable)
  • [ ] Payment history shows all attempts (including failed)
  • [ ] User can download payment receipt

API Endpoints: GET /payments/:id

Response:

{
  "paymentId": "uuid",
  "bookingId": "uuid",
  "status": "COMPLETED",
  "amount": 300000,
  "currency": "VND",
  "method": "STRIPE",
  "transactionId": "pi_1234567890",
  "paidAt": "2024-01-20T08:30:00Z",
  "refundAmount": 0
}

Estimated Effort: 3 story points


FR-4.4: Refund Processing

ID: FR-4.4
Priority: P1 (High)
User Story: As a customer, I want to receive refunds automatically when I cancel a booking.

Acceptance Criteria:

  • [ ] Automatic refund triggered when booking is cancelled
  • [ ] Refund amount calculated based on cancellation policy
  • [ ] Manual refund available for admin (full or partial)
  • [ ] Refund to original payment method (Stripe, VNPay)
  • [ ] Refund processing time: 5-7 business days (gateway dependent)
  • [ ] User notified via email when refund is initiated
  • [ ] User notified via email when refund is completed
  • [ ] Refund status tracked (pending, completed, failed)
  • [ ] Failed refunds are retried automatically
  • [ ] Admin can retry failed refunds manually
  • [ ] Refund transaction logged for audit

Refund Policy (reminder):

  • 48h before: 100% refund

  • 24-48h before: 50% refund
  • <24h before: 0% refund

API Endpoints:

  • POST /payments/:id/refund (automatic, called by Booking Service)
  • POST /payments/:id/refund/manual (admin only)

Estimated Effort: 8 story points


FR-4.5: Payment Receipts

ID: FR-4.5
Priority: P1 (High)
User Story: As a customer, I want a payment receipt for tax and accounting purposes.

Acceptance Criteria:

  • [ ] Generate PDF receipt with invoice format
  • [ ] Include VAT information (10% VAT in Vietnam)
  • [ ] Include company tax ID (for field owner)
  • [ ] Include payment details:
  • [ ] Invoice number (unique)
  • [ ] Payment date
  • [ ] Payment method
  • [ ] Transaction ID
  • [ ] Amount breakdown (subtotal, VAT, total)
  • [ ] Include customer information (name, email)
  • [ ] Include booking information (field, date, time)
  • [ ] Email receipt automatically after payment
  • [ ] Allow download from payment history
  • [ ] Receipt is PDF/A format (archival standard)

API Endpoints: GET /payments/:id/receipt

Response: PDF file (application/pdf)

Estimated Effort: 5 story points


FR-5: Notifications

FR-5.1: Email Notifications

ID: FR-5.1
Priority: P1 (High)
User Story: As a user, I want to receive email notifications for important events.

Email Types:

  1. Welcome Email (after registration)
  2. Welcome message
  3. Email verification link
  4. Getting started guide

  5. Email Verification (after registration)

  6. Verification link (24h expiry)
  7. Instructions

  8. Booking Confirmation (after payment)

  9. Booking details
  10. Field information
  11. Directions link
  12. Cancellation policy
  13. Receipt attached

  14. Payment Confirmation (after payment)

  15. Payment amount
  16. Transaction ID
  17. Receipt link

  18. Booking Reminder (24h and 2h before)

  19. Booking details
  20. Directions
  21. Contact information

  22. Cancellation Confirmation (after cancellation)

  23. Cancellation details
  24. Refund information
  25. Refund timeline

  26. Refund Notification (after refund)

  27. Refund amount
  28. Refund method
  29. Processing time

Acceptance Criteria:

  • [ ] All emails use branded HTML templates
  • [ ] Templates are responsive (mobile-friendly)
  • [ ] Include unsubscribe link (footer)
  • [ ] Emails sent within 1 minute of event
  • [ ] Track email delivery status (sent, delivered, bounced, opened)
  • [ ] Failed emails are retried (max 3 attempts)
  • [ ] User can customize notification preferences
  • [ ] Emails are sent asynchronously (BullMQ queue)

Technical:

  • Nodemailer with SMTP
  • Handlebars for templates
  • BullMQ for async sending
  • Store templates in database (Phase 2)

Estimated Effort: 8 story points


FR-5.2: SMS Notifications

ID: FR-5.2
Priority: P2 (Medium)
User Story: As a user, I want to receive SMS for urgent updates.

SMS Types:

  1. Booking Confirmation
  2. "Booking confirmed for [Field] on [Date] at [Time]. Total: [Price] VND"

  3. Booking Reminder (2h before)

  4. "Reminder: Your booking at [Field] starts in 2 hours at [Time]"

  5. Cancellation Confirmation

  6. "Booking cancelled for [Field] on [Date]. Refund: [Amount] VND"

Acceptance Criteria:

  • [ ] SMS sent to verified phone numbers only
  • [ ] Include booking ID for reference
  • [ ] Character limit: 160 characters (1 SMS unit)
  • [ ] Cost optimization: Only critical notifications
  • [ ] User can opt-out of SMS notifications
  • [ ] SMS status tracked (sent, delivered, failed)
  • [ ] Failed SMS are retried (max 2 attempts)
  • [ ] SMS sent asynchronously (BullMQ queue)

Technical:

  • Twilio API
  • SMS templates with placeholders
  • Cost tracking per SMS

Estimated Effort: 5 story points


FR-5.3: Push Notifications

ID: FR-5.3
Priority: P3 (Low)
User Story: As a mobile app user, I want push notifications for instant updates.

Status: Deferred to Phase 2 (requires mobile app)


FR-6: Search & Discovery

ID: FR-6.1
Priority: P1 (High)
User Story: As a visitor, I want to quickly find fields near me from the homepage.

Acceptance Criteria:

  • [ ] Search widget on homepage (prominent placement)
  • [ ] Detect user location via browser geolocation API
  • [ ] Allow manual location entry (address search)
  • [ ] Quick filters accessible without clicking:
  • [ ] Sport type (5v5, 7v7, 11v11)
  • [ ] Date picker (today, tomorrow, custom)
  • [ ] Price range slider
  • [ ] "Search" button triggers search
  • [ ] Show top 10 nearby fields on map
  • [ ] Show results in list view
  • [ ] One-click booking (quick book button)
  • [ ] Save search preferences in localStorage

Business Rules:

  • Default date: Today
  • Default radius: 5 km
  • Default sort: Distance (nearest first)

Estimated Effort: 5 story points


FR-6.2: Advanced Filters

ID: FR-6.2
Priority: P2 (Medium)
User Story: As a customer, I want to filter search results by multiple criteria.

Filters:

  • Distance (1km, 3km, 5km, 10km, 20km)
  • Price range (slider: 0 - 1,000,000 VND)
  • Sport type (checkboxes: 5v5, 7v7, 11v11)
  • Amenities (checkboxes: parking, shower, changing room, lighting, food court)
  • Operating hours (open now, open 24h)
  • Rating (Phase 2: 4+ stars, 3+ stars)
  • Availability (available now, available today)

Acceptance Criteria:

  • [ ] Filter panel on left side of search results
  • [ ] Combine multiple filters (AND logic)
  • [ ] Real-time result updates (no page refresh)
  • [ ] Show filter count badge (e.g., "3 filters applied")
  • [ ] Clear all filters button
  • [ ] Save filter preferences (localStorage)
  • [ ] Filter state persists on page refresh
  • [ ] Mobile: Filters in bottom sheet

Estimated Effort: 5 story points


FR-6.3: Sorting Options

ID: FR-6.3
Priority: P2 (Medium)
User Story: As a customer, I want to sort search results by different criteria.

Sort Options:

  • Distance (nearest first, farthest first)
  • Price (low to high, high to low)
  • Rating (highest first) - Phase 2
  • Popularity (most booked) - Phase 2
  • Newest (recently added)

Acceptance Criteria:

  • [ ] Sort dropdown in results header
  • [ ] Default sort: Distance (nearest first)
  • [ ] Results update immediately on sort change
  • [ ] Remember last sort preference (localStorage)
  • [ ] Sort order shown in dropdown (arrow icons)
  • [ ] Fast sorting (< 100ms)

Estimated Effort: 3 story points


Summary

Requirements Count by Category

Category Total P0 P1 P2 P3
User Management 6 3 2 1 0
Field Management 5 3 2 0 0
Booking Management 8 4 3 1 0
Payment Processing 5 2 3 0 0
Notifications 3 0 1 1 1
Search & Discovery 3 0 1 2 0
TOTAL 30 12 12 5 1

Estimated Total Effort

  • MVP (P0 + P1): ~150 story points (~15 sprints / 30 weeks with team of 2)
  • Phase 1 Complete (+ P2): ~175 story points (~17 sprints / 34 weeks)

MVP Scope (Must Have for Launch)

P0 Requirements (12):

  • FR-1.1, FR-1.2, FR-1.4 (User auth & roles)
  • FR-2.1, FR-2.3, FR-2.4 (Field listing & search)
  • FR-3.1, FR-3.4, FR-3.5 (Booking creation & conflicts)
  • FR-4.1, FR-4.2 (Payment integration)

P1 Requirements (12):

  • FR-1.3, FR-1.5 (Profile & password reset)
  • FR-2.2, FR-2.5 (Field management & details)
  • FR-3.2, FR-3.3, FR-3.8 (Booking history, cancel, receipt)
  • FR-4.3, FR-4.4, FR-4.5 (Payment tracking & refunds)
  • FR-5.1 (Email notifications)
  • FR-6.1 (Homepage search)

Document Version: 1.0
Last Updated: 2025-01-13
Next Review: 2025-02-13
Owner: Product Owner