link,[object Object]
Skip to content

Offers System ​

Overview ​

The offers system enables buyers to submit purchase proposals and sellers to manage negotiations. It includes offer submission, response management, counter-offers, and automatic deal creation upon acceptance.

Offer Lifecycle ​

mermaid
graph LR
    A[Offer Submitted] --> B[Pending Review]
    B --> C{Seller Decision}
    C -->|Accept| D[Deal Created]
    C -->|Reject| E[Rejected]
    C -->|Counter| F[Counter Offer]
    F --> G[Buyer Decision]
    G -->|Accept| D
    G -->|Reject| E
    G -->|Counter| H[New Counter]

Core Components ​

Offer Submission ​

File: src/components/offers/OfferForm.tsxFeatures:

  • Price input with validation
  • Terms and conditions specification
  • Message to seller
  • Financing information (if applicable)
  • Due diligence period selection

Offer Management Dashboard ​

File: src/pages/MyOffers.tsx (Buyer View) File: src/pages/seller/ManageOffers.tsx (Seller View) Features:

  • Offer status tracking
  • Response management
  • Deal progression monitoring
  • Communication history

Data Model ​

Core Table: offers ​

sql
id uuid PRIMARY KEY
listing_id uuid REFERENCES listings(id)
buyer_id uuid REFERENCES profiles(id)
seller_id uuid REFERENCES profiles(id)
amount numeric NOT NULL
status offer_status DEFAULT 'pending'
message text -- Buyer's initial message
response_message text -- Seller's response
created_at timestamptz DEFAULT now()
updated_at timestamptz DEFAULT now()

Offer Status Types ​

sql
CREATE TYPE offer_status AS ENUM (
  'pending',     -- Awaiting seller response
  'accepted',    -- Seller accepted, deal created
  'rejected',    -- Seller declined
  'countered',   -- Seller made counter-offer
  'withdrawn',   -- Buyer withdrew offer
  'expired'      -- Offer expired (time-based)
);

Offer Submission Process ​

Buyer Workflow ​

  1. Browse Listing: Find business of interest
  2. Submit Offer: Complete offer form with terms
  3. Wait for Response: Seller has 7 days to respond
  4. Negotiate: Exchange counter-offers if needed
  5. Deal Creation: Automatic upon acceptance

Validation Rules ​

typescript
const offerValidation = z.object({
  amount: z.number().min(1000).max(10000000), // $1K - $10M range
  message: z.string().min(50).max(1000), // Meaningful communication
  buyerRole: z.enum(['individual', 'company', 'investor']),
  experience: z.string().optional(),
  timeline: z.enum(['immediate', '30days', '60days']),
  financing: z.enum(['cash', 'sba_loan', 'seller_financing'])
})

Offer Form Implementation ​

typescript
const OfferForm = ({ listing }: { listing: Listing }) => {
  const { mutate: submitOffer } = useMutation({
    mutationFn: async (offerData: OfferSubmission) => {
      const { data, error } = await supabase
        .from('offers')
        .insert({
          listing_id: listing.id,
          buyer_id: user.id,
          seller_id: listing.seller_id,
          amount: offerData.amount,
          message: offerData.message
        })
      
      if (error) throw error
      return data
    },
    onSuccess: () => {
      toast.success('Offer submitted successfully!')
      navigate('/my-offers')
    }
  })
  
  // Form implementation...
}

Seller Response System ​

Response Options ​

  1. Accept Offer: Creates deal automatically
  2. Reject Offer: Declines with optional message
  3. Counter Offer: Proposes different terms
  4. Request Information: Ask for more buyer details

Seller Response Functions ​

RPC Functions:

  • seller_accept_offer(offer_id, response_message?): Accept and create deal
  • seller_reject_offer(offer_id, response_message?): Decline offer
  • seller_send_message(offer_id, message): Send message without status change
typescript
// Accept offer implementation
const acceptOffer = async (offerId: string, responseMessage?: string) => {
  const { data, error } = await supabase.rpc('seller_accept_offer', {
    p_offer_id: offerId,
    p_response_message: responseMessage
  })
  
  if (error) throw error
  
  // Returns transaction ID for new deal
  navigate(`/deal/${data}`)
}

Notification System ​

Automatic Notifications ​

Trigger: create_offer_notification()Events:

  • Offer Submitted: Notify seller of new offer
  • Offer Accepted: Notify buyer of acceptance + deal creation
  • Offer Rejected: Notify buyer of rejection
  • Offer Message: Notify about new communications
sql
-- Example notification creation
INSERT INTO notifications (user_id, type, message, link)
VALUES (
  seller_id,
  'offer',
  'New offer of €' || amount || ' for "' || listing_title || '"',
  '/manage-offers'
);

Email Integration ​

Service: Resend API for transactional emails Templates:

  • New offer received (to seller)
  • Offer status update (to buyer)
  • Counter-offer notification (to both parties)
  • Deal creation confirmation (to both parties)

Counter-Offer System ​

Counter-Offer Flow ​

  1. Seller Proposes: New price and terms
  2. Buyer Reviews: Consider counter-proposal
  3. Decision Making: Accept, reject, or counter again
  4. Negotiation Limit: Maximum 3 rounds to prevent endless loops

Implementation ​

typescript
const createCounterOffer = async (originalOfferId: string, newAmount: number, message: string) => {
  // Update original offer status to 'countered'
  await supabase
    .from('offers')
    .update({ 
      status: 'countered',
      response_message: message 
    })
    .eq('id', originalOfferId)
  
  // Create new offer with counter terms
  const { data } = await supabase
    .from('offers')
    .insert({
      listing_id: originalOffer.listing_id,
      buyer_id: originalOffer.buyer_id,
      seller_id: originalOffer.seller_id,
      amount: newAmount,
      message: `Counter-offer: ${message}`,
      parent_offer_id: originalOfferId // Track negotiation chain
    })
}

Offer Analytics ​

Key Metrics ​

  • Offer Conversion Rate: Percentage of offers that become deals
  • Average Response Time: How quickly sellers respond
  • Price Negotiation: Difference between asking price and final price
  • Offer Volume: Number of offers per listing

Analytics Implementation ​

typescript
const offerAnalytics = {
  conversionRate: async (sellerId: string) => {
    const offers = await supabase
      .from('offers')
      .select('status')
      .eq('seller_id', sellerId)
    
    const accepted = offers.filter(o => o.status === 'accepted').length
    return (accepted / offers.length) * 100
  },
  
  averageResponseTime: async (sellerId: string) => {
    // Calculate time between offer creation and first response
  },
  
  priceNegotiation: async (listingId: string) => {
    // Analyze final accepted price vs. listing price
  }
}

Security and Validation ​

Access Control ​

RLS Policies:

sql
-- Buyers can insert offers for others' listings
CREATE POLICY "Offers buyer can insert" 
ON offers FOR INSERT 
TO authenticated
WITH CHECK (
  buyer_id = auth.uid() 
  AND buyer_id <> seller_id -- Prevent self-offers
);

-- Users can view their own offers (as buyer or seller)
CREATE POLICY "Offers select own or admin" 
ON offers FOR SELECT 
TO authenticated
USING (
  buyer_id = auth.uid() 
  OR seller_id = auth.uid() 
  OR is_admin()
);

Anti-Spam Measures ​

  • Rate Limiting: Maximum 3 offers per user per day
  • Duplicate Prevention: Cannot submit multiple offers on same listing
  • Minimum Message Length: Encourage meaningful communication
  • Account Age Requirement: New accounts have limited offer capabilities

Fraud Prevention ​

typescript
// Offer validation checks
const validateOffer = async (offerData: OfferSubmission) => {
  // Check if buyer has sufficient verification
  if (offerData.amount > 50000 && !buyer.kyc_verified) {
    throw new Error('KYC verification required for offers over $50K')
  }
  
  // Prevent unrealistic offers
  if (offerData.amount < listing.price * 0.1) {
    throw new Error('Offer too low - minimum 10% of asking price')
  }
  
  // Check buyer subscription level for high-value offers
  if (offerData.amount > 100000 && buyer.subscription_level === 'free') {
    throw new Error('Paid subscription required for offers over $100K')
  }
}

Integration Points ​

With Listings System ​

  • Offer Count: Display number of offers on listing
  • Offer Buttons: Contextual call-to-action based on user state
  • Price Validation: Ensure offers are reasonable relative to asking price

With Messaging System ​

  • Automatic Conversations: Create message thread upon offer submission
  • Structured Communication: Offer-specific message templates
  • Notification Integration: Link offer updates to messaging notifications

With Deals System ​

  • Automatic Deal Creation: Upon offer acceptance
  • Data Transfer: Offer terms become deal foundation
  • Timeline Integration: Offer acceptance triggers deal timeline

Performance Optimization ​

Database Optimization ​

sql
-- Indexes for offer queries
CREATE INDEX idx_offers_buyer ON offers(buyer_id, created_at DESC);
CREATE INDEX idx_offers_seller ON offers(seller_id, status, created_at DESC);
CREATE INDEX idx_offers_listing ON offers(listing_id, status);

Caching Strategy ​

  • Offer Lists: 30-second cache for dashboard views
  • Offer Details: Fresh data to ensure accuracy
  • Offer Counts: 5-minute cache for listing displays
  • Analytics: 1-hour cache for metrics calculations

Testing Strategy ​

Unit Tests ​

  • Offer submission validation
  • Status transition logic
  • Price calculation accuracy
  • Notification trigger correctness

Integration Tests ​

  • Complete offer-to-deal workflow
  • Multi-party counter-offer negotiations
  • Notification delivery verification
  • RLS policy enforcement

E2E Tests ​

  • Buyer offer submission flow
  • Seller response management
  • Deal creation automation
  • Email notification delivery

Common Issues and Troubleshooting ​

Offer Submission Failures ​

Problem: Form submission errors Solutions:

  • Verify user authentication status
  • Check subscription level requirements
  • Validate offer amount against business rules
  • Confirm listing availability

Missing Notifications ​

Problem: Users not receiving offer notifications Solutions:

  • Verify notification trigger functions
  • Check email service configuration
  • Confirm user notification preferences
  • Review RLS policies for notifications table

Deal Creation Issues ​

Problem: Accepted offers not creating deals Solutions:

  • Check seller_accept_offer RPC function
  • Verify transaction table permissions
  • Confirm deal creation trigger logic
  • Review commission calculation functions

Best Practices ​

For Buyers ​

  1. Research Thoroughly: Review all available business information
  2. Meaningful Communication: Provide detailed offer rationale
  3. Realistic Pricing: Base offers on business metrics and market analysis
  4. Professional Approach: Maintain courteous and business-like communication

For Sellers ​

  1. Timely Responses: Respond to offers within 48 hours
  2. Clear Communication: Provide specific feedback on offers
  3. Fair Evaluation: Consider all aspects of buyer's proposal
  4. Professional Documentation: Maintain records of all negotiations

For Platform ​

  1. Clear Guidelines: Provide offer submission best practices
  2. Dispute Resolution: Establish clear escalation procedures
  3. Performance Monitoring: Track conversion rates and user satisfaction
  4. Continuous Improvement: Regular review and optimization of offer flow

Future Enhancements ​

  1. AI-Powered Valuation: Suggest fair offer prices based on business metrics
  2. Offer Templates: Pre-filled forms for common offer types
  3. Bulk Offers: Allow buyers to submit offers on multiple listings
  4. Advanced Analytics: Detailed offer performance and conversion tracking
  5. Mobile Optimization: Enhanced mobile experience for offer management

Related Documentation:

  • Deals System - What happens after offer acceptance
  • Messaging - Communication during negotiations
  • Listings - How offers integrate with business listings