Source Code

(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-sold-out (err u102))
(define-constant err-invalid-rating (err u103))

(define-map experiences
  uint
  {
    host: principal,
    title: (string-ascii 128),
    category: (string-ascii 64),
    location: (string-ascii 256),
    duration-blocks: uint,
    max-participants: uint,
    booked: uint,
    price-per-person: uint,
    active: bool
  })

(define-map bookings
  {experience-id: uint, booking-id: uint}
  {guest: principal, participants: uint, booked-at: uint, completed: bool})

(define-map reviews
  {experience-id: uint, reviewer: principal}
  {rating: uint, comment: (string-ascii 256), timestamp: uint})

(define-data-var next-experience-id uint u0)

(define-read-only (get-experience (experience-id uint))
  (ok (map-get? experiences experience-id)))

(define-read-only (get-booking (experience-id uint) (booking-id uint))
  (ok (map-get? bookings {experience-id: experience-id, booking-id: booking-id})))

(define-public (create-experience (title (string-ascii 128)) (category (string-ascii 64)) (location (string-ascii 256)) (duration uint) (max uint) (price uint))
  (let ((experience-id (var-get next-experience-id)))
    (map-set experiences experience-id
      {host: tx-sender, title: title, category: category, location: location,
       duration-blocks: duration, max-participants: max, booked: u0,
       price-per-person: price, active: true})
    (var-set next-experience-id (+ experience-id u1))
    (ok experience-id)))

(define-public (book-experience (experience-id uint) (num-participants uint))
  (let ((experience (unwrap! (map-get? experiences experience-id) err-not-found))
        (booking-id (get booked experience))
        (total-price (* (get price-per-person experience) num-participants)))
    (asserts! (get active experience) err-not-found)
    (asserts! (<= (+ (get booked experience) num-participants) (get max-participants experience)) err-sold-out)
    (try! (stx-transfer? total-price tx-sender (get host experience)))
    (map-set bookings {experience-id: experience-id, booking-id: booking-id}
      {guest: tx-sender, participants: num-participants, booked-at: stacks-block-height, completed: false})
    (ok (map-set experiences experience-id
      (merge experience {booked: (+ (get booked experience) num-participants)})))))

(define-public (complete-booking (experience-id uint) (booking-id uint))
  (let ((experience (unwrap! (map-get? experiences experience-id) err-not-found))
        (booking (unwrap! (map-get? bookings {experience-id: experience-id, booking-id: booking-id}) err-not-found)))
    (asserts! (is-eq tx-sender (get host experience)) err-owner-only)
    (ok (map-set bookings {experience-id: experience-id, booking-id: booking-id}
      (merge booking {completed: true})))))

(define-public (leave-review (experience-id uint) (rating uint) (comment (string-ascii 256)))
  (begin
    (asserts! (<= rating u5) err-invalid-rating)
    (ok (map-set reviews {experience-id: experience-id, reviewer: tx-sender}
      {rating: rating, comment: comment, timestamp: stacks-block-height}))))

Functions (6)

FunctionAccessArgs
get-experienceread-onlyexperience-id: uint
get-bookingread-onlyexperience-id: uint, booking-id: uint
create-experiencepublictitle: (string-ascii 128
book-experiencepublicexperience-id: uint, num-participants: uint
complete-bookingpublicexperience-id: uint, booking-id: uint
leave-reviewpublicexperience-id: uint, rating: uint, comment: (string-ascii 256