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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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
Copy
// 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:- Build Trust through blockchain-verified reputation
- Improve Quality by enabling informed hiring decisions
- Reduce Risk with transparent provider histories
- Create Network Effects through portable, cross-platform reputation
- Ensure Fairness with deal-bound rating systems
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.