Skip to content

Marketplace & Orders

RoutePurpose
/marketplaceBrowse and purchase services
/marketplace/order-success/[sessionId]Post-payment confirmation
/ordersManage purchases and sales

  • Image carousel (main + media array)
  • Hub logo, name, and handle
  • Service name and description (2-line clamp)
  • Category/subcategory badges
  • Up to 3 tags (with “+X more” tooltip)
  • Starting price (from cents, divided by 100)
  • Minimum delivery days
  • Package count
  • Compare checkbox, favorite heart, message button
FilterDescription
CategoryGraphics & Design, Programming, Writing, Video, Music, Business, Personal, Other
SubcategoryContext-dependent on category
SearchText search across services
FavoritesToggle to show only favorited services

Full service details: image carousel, hub info, description, tags, requirements, FAQs, package tiers (Basic/Standard/Premium) with pricing, delivery, and revisions.

  • Select up to 10 services via compare checkboxes
  • Side-by-side comparison: price, delivery, packages, requirements
  • “View Details” opens quick view per service

Stored at: users/{userId}/favorites/{serviceId}

{
serviceId, hubId, serviceName, serviceImage,
hubHandle, hubName, addedAt: serverTimestamp()
}

Toggle via heart icon on service cards.


User clicks “Select Package” in quick view modal. Package data includes: tier, name, price, deliveryDays, revisions, features, stripePriceId.

Left panel: Package details (tier, name, price, delivery, revisions, features)

Center: Project requirements form

FieldRequiredValidation
Project RequirementsYesMin 10 characters
Additional NotesNoNo limit

Right panel (sticky): Price breakdown (package price + platform fee + total), “Continue to Payment” button.

Platform fee: Currently 0% (previously 3-12% based on seller plan).

On “Continue to Payment”:

  1. Validates requirements >= 10 chars
  2. Fetches seller’s Stripe Connect account from stripeAccounts/{sellerId}
  3. Calls createConnectCheckoutCallable Cloud Function with:
{
priceId, sellerStripeAccountId, amountInCents,
successUrl: "{origin}/marketplace/order-success",
cancelUrl: "{origin}/marketplace",
platformFeePercentage: 0,
currency: 'USD',
buyerId: currentUser.uid,
metadata: {
serviceId, serviceName, serviceImageUrl, hubId,
packageId, packageName, packageTier, sellerId,
sellerName, requirements, additionalNotes,
deliveryDays, revisions, isSinglePrice
}
}
  1. Returns { sessionId, url, orderId, applicationFeeAmount }
  2. Redirects to Stripe Checkout URL

Route: /marketplace/order-success/[sessionId]

StatusDisplay
completedGreen checkmark, “Order Confirmed!”, service/package/seller details, buttons: Continue Shopping + View Orders
failedRed alert, “Payment Failed”, error message, Back to Marketplace button
pendingLoading spinner, “Processing Payment…”

  • Purchases: Orders where current user is buyer
  • Sales: Orders where current user is seller
  • Pending count, Completed count, Disputed count
  • Total Spent (purchases) / Total Earned (sales)
  • By status: pending, in_progress, completed, cancelled, refunded, disputed
  • By search query (service name, seller/buyer name)
ActionPurchasesSales
View DetailsYesYes
MessageMessage SellerMessage Buyer
Report IssueYes (if not cancelled)No
View DisputeYes (if exists)Yes (if exists)

FieldRequiredValidation
DescriptionYesMin 20 characters
Evidence filesNoMax 5 files, max 10MB each

Accepted file types: JPEG, PNG, GIF, WebP, PDF

Evidence upload path: disputes/{orderId}/{fileId}_{fileName}

open → in_review → resolved
  • fee_refunded - Platform fee refunded
  • handle_directly - Parties resolve directly
  1. Dispute document created at marketplace_disputes/{disputeId}
  2. Order updated with disputeId field
  3. System message sent to other party via chat
  4. Email notifications generated for admin, buyer, and seller
{
id, orderId, reporterId, reporterRole: 'buyer'|'seller',
description, evidence: [{id, url, name, type, size, uploadedAt}],
status: 'open'|'in_review'|'resolved',
resolution: 'fee_refunded'|'handle_directly'|null,
resolvedAt?, resolvedBy?, adminNotes?,
createdAt, updatedAt
}

Collection: marketplace_orders

{
id, amountInCents, applicationFeeAmount,
buyerId, sellerId, serviceId, packageId,
checkoutSessionId, stripeAccountId,
status: 'pending'|'in_progress'|'completed'|'cancelled'|'refunded',
disputeId?: string|null,
createdAt, updatedAt
}

BlockerDescription
Seller no Stripe account”Seller has not set up payments yet”
Requirements < 10 charsPayment button disabled
Max 5 evidence filesAdditional uploads rejected
Evidence file > 10MBUpload rejected
Open dispute existsCannot file another dispute for same order