Skip to main content

Service Platform Integration

Learn how to integrate IOTA Repstation into a freelancing and service marketplace platform where clients and service providers build reputation through completed projects.

Overview

This example demonstrates integrating IOTA Repstation into SkillForge - a comprehensive service marketplace where:
  • Clients post projects and hire service providers
  • Service Providers offer skills (design, development, writing, consulting, etc.)
  • Projects have clear deliverables and deadlines
  • Reputation is built through successful project completions
  • Trust is established through transparent, blockchain-verified ratings

Key Use Cases

Freelancer Marketplace

Web development, design, content creation, digital marketing

Professional Services

Legal, accounting, consulting, business strategy

Creative Services

Graphic design, video editing, photography, writing

Technical Services

IT support, software development, data analysis

Architecture Overview

Implementation Guide

1. Platform Setup

// server/reputation.ts
import { RepstationClient } from '@repstation/sdk';

const repstationClient = new RepstationClient({
  network: 'mainnet',
  packageId: process.env.REPSTATION_PACKAGE_ID,
  registryId: process.env.REPSTATION_REGISTRY_ID,
  aggregatesId: process.env.REPSTATION_AGGREGATES_ID,
});

// Register platform
export async function initializePlatform() {
  await repstationClient.registerApp({
    appId: 'com.skillforge.platform',
    metadataUri: 'ipfs://platform-metadata',
    adminSigner: process.env.ADMIN_PRIVATE_KEY,
  });
}

2. Project Lifecycle Integration

Project Creation & Bidding

// types/project.ts
interface Project {
  id: string;
  title: string;
  description: string;
  budget: number;
  deadline: Date;
  skills: string[];
  clientId: string;
  status: 'open' | 'in_progress' | 'completed' | 'cancelled';
  reputationDealId?: string;
}

interface ServiceProvider {
  id: string;
  walletAddress: string;
  skills: string[];
  hourlyRate: number;
  reputation: {
    global: { average: number; count: number };
    platform: { average: number; count: number };
  };
}

Deal Creation When Project Awarded

// services/projectService.ts
export async function awardProject(
  projectId: string,
  serviceProviderId: string,
  agreedPrice: number
) {
  const project = await db.projects.findById(projectId);
  const serviceProvider = await db.users.findById(serviceProviderId);
  
  // Create deal in reputation system
  const { dealId } = await repstationClient.openDealAdmin({
    adminCapId: process.env.ADMIN_CAP_ID,
    partyB: serviceProvider.walletAddress,
    subjectRef: {
      type: 'PROJECT',
      id: projectId,
      title: project.title,
      category: project.skills[0]
    },
    amount: agreedPrice,
    signer: process.env.ADMIN_PRIVATE_KEY,
  });

  // Update project with deal ID
  await db.projects.update(projectId, {
    status: 'in_progress',
    serviceProviderId,
    agreedPrice,
    reputationDealId: dealId,
    awardedAt: new Date(),
  });

  // Notify service provider
  await notificationService.send(serviceProviderId, {
    type: 'project_awarded',
    projectId,
    dealId,
    message: `Congratulations! You've been awarded the project "${project.title}".`
  });

  return { project, dealId };
}

Service Provider Accepts Project

// api/projects/accept.ts
export async function acceptProject(req: Request, res: Response) {
  const { projectId } = req.params;
  const { serviceProviderId, signature } = req.body;

  try {
    const project = await db.projects.findById(projectId);
    
    if (!project.reputationDealId) {
      throw new Error('No reputation deal found for project');
    }

    // Accept deal in reputation system
    await repstationClient.acceptDeal({
      dealId: project.reputationDealId,
      signer: signature, // Service provider's signature
    });

    // Update project status
    await db.projects.update(projectId, {
      acceptedAt: new Date(),
      status: 'active'
    });

    // Create project workspace
    await workspaceService.createProjectWorkspace(projectId);

    res.json({ 
      success: true, 
      message: 'Project accepted successfully',
      workspace: `/projects/${projectId}/workspace`
    });

  } catch (error) {
    res.status(400).json({ error: error.message });
  }
}

3. Project Completion & Rating System

Project Delivery & Approval

// services/deliveryService.ts
export async function submitDelivery(
  projectId: string,
  serviceProviderId: string,
  deliverables: {
    files: File[];
    description: string;
    completionNotes: string;
  }
) {
  const project = await db.projects.findById(projectId);
  
  // Store deliverables
  const deliveryId = await storage.saveDelivery({
    projectId,
    serviceProviderId,
    files: deliverables.files,
    description: deliverables.description,
    submittedAt: new Date(),
  });

  // Update project
  await db.projects.update(projectId, {
    status: 'pending_approval',
    deliveryId,
    submittedAt: new Date(),
  });

  // Notify client
  await notificationService.send(project.clientId, {
    type: 'delivery_submitted',
    projectId,
    deliveryId,
    message: `${serviceProvider.name} has submitted project deliverables for review.`
  });

  return deliveryId;
}

export async function approveDelivery(
  projectId: string,
  clientId: string,
  approval: {
    approved: boolean;
    feedback?: string;
    revisionRequests?: string[];
  }
) {
  const project = await db.projects.findById(projectId);

  if (approval.approved) {
    // Close deal in reputation system (enables rating)
    await repstationClient.closeDeal({
      dealId: project.reputationDealId,
      signer: clientId, // Client closes the deal
    });

    // Process payment
    await paymentService.releaseEscrow(projectId);

    // Update project
    await db.projects.update(projectId, {
      status: 'completed',
      completedAt: new Date(),
      clientFeedback: approval.feedback,
    });

    // Start rating period
    await initiateRatingPeriod(projectId);

  } else {
    // Request revisions
    await db.projects.update(projectId, {
      status: 'revision_requested',
      revisionRequests: approval.revisionRequests,
      revisionRequestedAt: new Date(),
    });
  }
}

Mutual Rating System

// components/RatingModal.tsx
import { RepstationClient } from '@repstation/sdk';

interface RatingModalProps {
  project: Project;
  raterType: 'client' | 'service_provider';
  onRatingSubmitted: () => void;
}

export function RatingModal({ project, raterType, onRatingSubmitted }: RatingModalProps) {
  const [ratings, setRatings] = useState({
    communication: 0,
    quality: 0,
    timeliness: 0,
    professionalism: 0,
  });
  const [comment, setComment] = useState('');
  const [submitting, setSubmitting] = useState(false);

  const submitRatings = async () => {
    setSubmitting(true);
    
    try {
      const repstationClient = new RepstationClient({
        network: 'mainnet',
        packageId: process.env.NEXT_PUBLIC_REPSTATION_PACKAGE_ID,
      });

      // Submit multiple ratings for different categories
      const ratingPromises = Object.entries(ratings).map(async ([category, score]) => {
        if (score > 0) {
          return await repstationClient.rate({
            dealId: project.reputationDealId,
            score: score * 20, // Convert 1-5 to 20-100 scale
            category,
            signer: await wallet.getSignature(),
          });
        }
      });

      const ratingResults = await Promise.all(ratingPromises.filter(Boolean));

      // Store in local database
      await fetch('/api/ratings', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          projectId: project.id,
          raterType,
          ratings,
          comment,
          endorsementIds: ratingResults.map(r => r?.endorsementId).filter(Boolean),
        }),
      });

      onRatingSubmitted();
      
    } catch (error) {
      console.error('Rating submission failed:', error);
      alert('Failed to submit rating. Please try again.');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="rating-modal">
      <h3>Rate Your Experience</h3>
      <p>
        {raterType === 'client' 
          ? 'How was working with this service provider?' 
          : 'How was working with this client?'
        }
      </p>

      <div className="rating-categories">
        {Object.entries(ratings).map(([category, value]) => (
          <div key={category} className="rating-row">
            <label>{category.charAt(0).toUpperCase() + category.slice(1)}</label>
            <StarRating
              value={value}
              onChange={(newValue) => 
                setRatings(prev => ({ ...prev, [category]: newValue }))
              }
            />
          </div>
        ))}
      </div>

      <div className="comment-section">
        <label>Additional Comments (Optional)</label>
        <textarea
          value={comment}
          onChange={(e) => setComment(e.target.value)}
          placeholder="Share your experience working on this project..."
          rows={4}
        />
      </div>

      <div className="modal-actions">
        <button 
          onClick={submitRatings}
          disabled={submitting || Object.values(ratings).every(r => r === 0)}
          className="submit-button"
        >
          {submitting ? 'Submitting...' : 'Submit Rating'}
        </button>
      </div>
    </div>
  );
}

4. Service Provider Profile & Reputation Display

// components/ServiceProviderProfile.tsx
interface ServiceProviderProfileProps {
  providerId: string;
}

export function ServiceProviderProfile({ providerId }: ServiceProviderProfileProps) {
  const [provider, setProvider] = useState<ServiceProvider | null>(null);
  const [reputation, setReputation] = useState(null);
  const [recentProjects, setRecentProjects] = useState([]);

  useEffect(() => {
    loadProviderData();
  }, [providerId]);

  const loadProviderData = async () => {
    // Load provider info
    const providerData = await fetch(`/api/providers/${providerId}`).then(r => r.json());
    setProvider(providerData);

    // Load reputation from blockchain
    const repstationClient = new RepstationClient({
      network: 'mainnet',
      packageId: process.env.NEXT_PUBLIC_REPSTATION_PACKAGE_ID,
    });

    const reputationData = await repstationClient.getReputationProfile({
      walletAddress: providerData.walletAddress,
    });
    
    setReputation(reputationData.profile);

    // Load recent projects
    const projects = await fetch(`/api/providers/${providerId}/projects`).then(r => r.json());
    setRecentProjects(projects);
  };

  if (!provider || !reputation) {
    return <div>Loading...</div>;
  }

  return (
    <div className="provider-profile">
      {/* Header */}
      <div className="profile-header">
        <div className="avatar">
          <img src={provider.avatarUrl} alt={provider.name} />
        </div>
        <div className="basic-info">
          <h1>{provider.name}</h1>
          <p className="title">{provider.title}</p>
          <p className="location">{provider.location}</p>
          <div className="hourly-rate">${provider.hourlyRate}/hour</div>
        </div>
      </div>

      {/* Reputation Stats */}
      <div className="reputation-section">
        <h3>Reputation & Reviews</h3>
        
        <div className="reputation-stats">
          <div className="global-reputation">
            <div className="score-circle">
              <span className="score">{reputation.global?.average || 'New'}</span>
              <span className="scale">/100</span>
            </div>
            <div className="score-details">
              <div className="label">Global Reputation</div>
              <div className="count">{reputation.global?.count || 0} total ratings</div>
              <div className="portable">Portable across all platforms</div>
            </div>
          </div>

          <div className="platform-reputation">
            <div className="score-circle platform">
              <span className="score">{reputation.appScoped?.[0]?.stats?.average || 'New'}</span>
              <span className="scale">/100</span>
            </div>
            <div className="score-details">
              <div className="label">SkillForge Reputation</div>
              <div className="count">{reputation.appScoped?.[0]?.stats?.count || 0} platform ratings</div>
            </div>
          </div>
        </div>

        {/* Category Breakdown */}
        <div className="category-ratings">
          <h4>Category Ratings</h4>
          <div className="categories">
            {provider.categoryRatings?.map((category) => (
              <div key={category.name} className="category">
                <span className="category-name">{category.name}</span>
                <div className="rating-bar">
                  <div 
                    className="fill" 
                    style={{ width: `${(category.average / 5) * 100}%` }}
                  />
                </div>
                <span className="score">{category.average.toFixed(1)}</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* Skills & Expertise */}
      <div className="skills-section">
        <h3>Skills & Expertise</h3>
        <div className="skills-grid">
          {provider.skills.map((skill) => (
            <div key={skill.name} className="skill-tag">
              <span className="skill-name">{skill.name}</span>
              <span className="skill-level">{skill.level}</span>
            </div>
          ))}
        </div>
      </div>

      {/* Recent Projects */}
      <div className="projects-section">
        <h3>Recent Projects</h3>
        <div className="projects-list">
          {recentProjects.map((project) => (
            <div key={project.id} className="project-card">
              <h4>{project.title}</h4>
              <p className="description">{project.description}</p>
              <div className="project-meta">
                <span className="budget">${project.budget}</span>
                <span className="completion">Completed {formatDate(project.completedAt)}</span>
                {project.clientRating && (
                  <div className="client-rating">
                    <StarDisplay rating={project.clientRating} />
                    <span>by {project.clientName}</span>
                  </div>
                )}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

5. Smart Filtering & Matching

// services/matchingService.ts
export async function findServiceProviders(
  projectRequirements: {
    skills: string[];
    budget: number;
    deadline: Date;
    experienceLevel: 'entry' | 'intermediate' | 'expert';
  },
  reputationFilters: {
    minGlobalReputation?: number;
    minPlatformReputation?: number;
    minProjectCount?: number;
    preferredCategories?: string[];
  }
) {
  // Get providers with required skills
  let providers = await db.serviceProviders.findBySkills(projectRequirements.skills);

  // Load reputation data for each provider
  const providersWithReputation = await Promise.all(
    providers.map(async (provider) => {
      const reputation = await repstationClient.getReputationProfile({
        walletAddress: provider.walletAddress,
      });

      return {
        ...provider,
        reputation: reputation.profile,
        matchScore: calculateMatchScore(provider, projectRequirements, reputation.profile),
      };
    })
  );

  // Apply reputation filters
  const filteredProviders = providersWithReputation.filter((provider) => {
    const globalRep = provider.reputation?.global?.average || 0;
    const platformRep = provider.reputation?.appScoped?.[0]?.stats?.average || 0;
    const projectCount = provider.reputation?.global?.count || 0;

    return (
      globalRep >= (reputationFilters.minGlobalReputation || 0) &&
      platformRep >= (reputationFilters.minPlatformReputation || 0) &&
      projectCount >= (reputationFilters.minProjectCount || 0)
    );
  });

  // Sort by match score and reputation
  return filteredProviders.sort((a, b) => {
    const scoreA = a.matchScore + (a.reputation?.global?.average || 0) * 0.1;
    const scoreB = b.matchScore + (b.reputation?.global?.average || 0) * 0.1;
    return scoreB - scoreA;
  });
}

function calculateMatchScore(
  provider: ServiceProvider, 
  requirements: any, 
  reputation: any
): number {
  let score = 0;

  // Skill match (40%)
  const skillMatch = requirements.skills.filter(skill => 
    provider.skills.some(ps => ps.name.toLowerCase() === skill.toLowerCase())
  ).length / requirements.skills.length;
  score += skillMatch * 40;

  // Budget compatibility (20%)
  const budgetFit = provider.hourlyRate <= (requirements.budget / 40); // Assume 40 hours
  score += budgetFit ? 20 : 0;

  // Experience level match (20%)
  const expMatch = matchExperienceLevel(provider.experienceLevel, requirements.experienceLevel);
  score += expMatch * 20;

  // Reputation bonus (20%)
  const reputationBonus = Math.min((reputation?.global?.average || 0) / 100 * 20, 20);
  score += reputationBonus;

  return score;
}

6. Advanced Features

Dispute Resolution

// services/disputeService.ts
export async function initiateDispute(
  projectId: string,
  initiatorId: string,
  reason: string,
  evidence: File[]
) {
  const project = await db.projects.findById(projectId);
  
  // Pause any pending ratings
  await db.projects.update(projectId, {
    status: 'disputed',
    disputeReason: reason,
    disputeInitiatedAt: new Date(),
    disputeInitiatorId: initiatorId,
  });

  // Store evidence
  const evidenceUrls = await storage.saveDisputeEvidence(projectId, evidence);

  // Create dispute record
  const dispute = await db.disputes.create({
    projectId,
    initiatorId,
    reason,
    evidenceUrls,
    status: 'open',
    createdAt: new Date(),
  });

  // Notify dispute resolution team
  await notificationService.notifyDispute(dispute);

  return dispute;
}

Reputation Analytics Dashboard

// components/ReputationDashboard.tsx
export function ReputationDashboard({ providerId }: { providerId: string }) {
  const [analytics, setAnalytics] = useState(null);
  const [trends, setTrends] = useState([]);

  useEffect(() => {
    loadAnalytics();
  }, [providerId]);

  const loadAnalytics = async () => {
    // Load reputation trends
    const trendsData = await fetch(`/api/providers/${providerId}/reputation-trends`).then(r => r.json());
    setTrends(trendsData);

    // Load detailed analytics
    const analyticsData = await fetch(`/api/providers/${providerId}/analytics`).then(r => r.json());
    setAnalytics(analyticsData);
  };

  return (
    <div className="reputation-dashboard">
      <h2>Reputation Analytics</h2>

      {/* Reputation Trend Chart */}
      <div className="chart-section">
        <h3>Reputation Over Time</h3>
        <ReputationTrendChart data={trends} />
      </div>

      {/* Category Performance */}
      <div className="categories-section">
        <h3>Performance by Category</h3>
        <div className="category-grid">
          {analytics?.categoryBreakdown?.map((category) => (
            <div key={category.name} className="category-card">
              <h4>{category.name}</h4>
              <div className="score">{category.averageScore}/100</div>
              <div className="trend">
                {category.trend > 0 ? '↗' : category.trend < 0 ? '↘' : '→'} 
                {Math.abs(category.trend)}%
              </div>
              <div className="count">{category.ratingCount} ratings</div>
            </div>
          ))}
        </div>
      </div>

      {/* Recent Feedback */}
      <div className="feedback-section">
        <h3>Recent Client Feedback</h3>
        <div className="feedback-list">
          {analytics?.recentFeedback?.map((feedback) => (
            <div key={feedback.id} className="feedback-item">
              <div className="rating">
                <StarDisplay rating={feedback.overallRating} />
              </div>
              <div className="comment">"{feedback.comment}"</div>
              <div className="project">{feedback.projectTitle}</div>
              <div className="date">{formatDate(feedback.date)}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Integration Benefits

For Platform Owners

Trust & Safety

Blockchain-verified reputation reduces fraud and builds platform trust

Quality Assurance

High-reputation providers deliver better results, improving platform quality

Reduced Disputes

Clear reputation history helps clients make better hiring decisions

Network Effects

Cross-platform reputation attracts quality providers from other platforms

For Service Providers

Portable Reputation

Reputation follows you across all integrated platforms

Competitive Advantage

High reputation leads to more project opportunities and higher rates

Fair Assessment

Deal-bound ratings ensure only completed projects affect reputation

Transparent History

Immutable blockchain record of all professional achievements

For Clients

Informed Decisions

Access to comprehensive, verified reputation data

Quality Assurance

Hire providers with proven track records

Risk Reduction

Transparent ratings help avoid unreliable providers

Fair Pricing

Reputation-based pricing reflects true value and quality

Advanced Implementation Examples

Escrow Integration

// services/escrowService.ts
export class ProjectEscrowService {
  async createEscrow(projectId: string, amount: number) {
    const project = await db.projects.findById(projectId);
    
    // Create escrow account
    const escrow = await paymentProvider.createEscrow({
      amount,
      releaseConditions: {
        requiresApproval: true,
        autoReleaseAfter: '30 days',
        disputeResolution: true,
      },
    });

    // Link to reputation deal
    await db.projects.update(projectId, {
      escrowId: escrow.id,
      escrowStatus: 'locked',
    });

    return escrow;
  }

  async releaseEscrow(projectId: string) {
    const project = await db.projects.findById(projectId);
    
    // Only release if deal is closed (project completed)
    if (project.status !== 'completed') {
      throw new Error('Cannot release escrow - project not completed');
    }

    await paymentProvider.releaseEscrow(project.escrowId);
    
    await db.projects.update(projectId, {
      escrowStatus: 'released',
      paymentReleasedAt: new Date(),
    });
  }
}

Skill Verification

// services/skillVerificationService.ts
export async function verifySkillThroughProject(
  providerId: string,
  skill: string,
  projectId: string
) {
  const project = await db.projects.findById(projectId);
  
  // Only verify skills for highly-rated completed projects
  if (project.status === 'completed' && project.clientRating >= 4.5) {
    await db.serviceProviders.addVerifiedSkill(providerId, {
      skill,
      verifiedThrough: 'project_completion',
      projectId,
      verifiedAt: new Date(),
      reputationDealId: project.reputationDealId,
    });
  }
}

Summary

The IOTA Repstation integration enables SkillForge to:
  1. Build Trust through blockchain-verified reputation
  2. Improve Quality by enabling informed hiring decisions
  3. Reduce Risk with transparent provider histories
  4. Create Network Effects through portable, cross-platform reputation
  5. Ensure Fairness with deal-bound rating systems
This creates a thriving ecosystem where quality service providers are rewarded, clients can hire with confidence, and the platform benefits from reduced disputes and increased user satisfaction.
Ready to integrate? Check out our Quickstart Guide to get up and running in 5 minutes, or explore the API Reference for detailed implementation guidance.