Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 137 additions & 6 deletions app/api/v1/paid/[product]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,128 @@ function paymentRequired(req: NextRequest, product: NonNullable<ReturnType<typeo
);
}

function payload(productId: string, req: NextRequest, proof: string) {
type GitHubRepo = {
full_name: string;
html_url: string;
description: string | null;
stargazers_count: number;
forks_count: number;
language: string | null;
updated_at: string;
topics?: string[];
owner?: { login: string; html_url: string };
};

function leadQuery(segment: string) {
switch (segment) {
case 'agent-frameworks':
return 'agent framework mcp OR x402 language:TypeScript pushed:>2026-01-01';
case 'api-tools':
return 'AI API x402 paid endpoint language:TypeScript pushed:>2026-01-01';
case 'mcp':
default:
return 'mcp server tools language:TypeScript pushed:>2026-01-01';
}
}

function scoreLead(repo: GitHubRepo, segment: string) {
const text = `${repo.full_name} ${repo.description || ''} ${(repo.topics || []).join(' ')}`.toLowerCase();
let score = 30;
if (text.includes('mcp')) score += 25;
if (text.includes('x402') || text.includes('paid') || text.includes('payment')) score += 20;
if (text.includes('agent') || text.includes('tool')) score += 15;
if (segment === 'agent-frameworks' && text.includes('framework')) score += 10;
if (segment === 'api-tools' && text.includes('api')) score += 10;
score += Math.min(10, Math.floor(Math.log10(Math.max(1, repo.stargazers_count)) * 5));
return Math.min(100, score);
}

function fallbackLeads(segment: string) {
const targets = [
{
name: 'MCP server vendors with data-heavy tools',
url: 'https://github.com/search?q=mcp+server+tools+language%3ATypeScript&type=repositories',
segment: 'mcp',
score: segment === 'mcp' ? 82 : 70,
reason: 'Already expose agent-callable tools; easiest path to wrap one high-value tool behind x402.',
},
{
name: 'Agent frameworks with plugin marketplaces',
url: 'https://github.com/search?q=agent+framework+mcp+x402&type=repositories',
segment: 'agent-frameworks',
score: segment === 'agent-frameworks' ? 80 : 68,
reason: 'Can route many downstream agents to paid tools through a single Pyrimid catalog integration.',
},
{
name: 'AI API wrappers with per-call cost',
url: 'https://github.com/search?q=AI+API+x402+paid+endpoint&type=repositories',
segment: 'api-tools',
score: segment === 'api-tools' ? 78 : 66,
reason: 'Usage-based API value maps cleanly to per-call Base USDC pricing and affiliate distribution.',
},
];

return targets.sort((a, b) => b.score - a.score);
}

async function vendorLeadDiscovery(segment: string) {
const query = leadQuery(segment);
const searchUrl = `https://api.github.com/search/repositories?q=${encodeURIComponent(query)}&sort=updated&order=desc&per_page=8`;

try {
const response = await fetch(searchUrl, {
headers: {
Accept: 'application/vnd.github+json',
'User-Agent': 'pyrimid-vendor-lead-discovery',
},
cache: 'no-store',
});

if (response.ok) {
const data = (await response.json()) as { items?: GitHubRepo[] };
const leads = (data.items || []).map((repo) => {
const score = scoreLead(repo, segment);
return {
name: repo.full_name,
url: repo.html_url,
owner: repo.owner?.login || repo.full_name.split('/')[0],
owner_url: repo.owner?.html_url || `https://github.com/${repo.full_name.split('/')[0]}`,
segment,
score,
language: repo.language,
stars: repo.stargazers_count,
forks: repo.forks_count,
updated_at: repo.updated_at,
reason:
score >= 75
? 'Strong fit for a paid MCP/API wrapper and Pyrimid catalog listing.'
: 'Potential fit; confirm tool surface, pricing unit, and owner interest before outreach.',
recommended_pitch: 'Offer a no-risk paid endpoint pilot with x402 402 metadata, Base USDC settlement, and affiliateBps for distribution agents.',
};
});

if (leads.length > 0) {
return {
source: 'github-search',
query,
generated_at: new Date().toISOString(),
leads,
};
}
}
} catch {
// Fall through to deterministic seed leads when GitHub search is unavailable.
}

return {
source: 'fallback-seed',
query,
generated_at: new Date().toISOString(),
leads: fallbackLeads(segment),
};
}

async function payload(productId: string, req: NextRequest, proof: string) {
const query = Object.fromEntries(req.nextUrl.searchParams.entries());

switch (productId) {
Expand Down Expand Up @@ -53,12 +174,22 @@ function payload(productId: string, req: NextRequest, proof: string) {
}
case 'vendor-lead-discovery': {
const segment = query.segment || 'mcp';
const discovery = await vendorLeadDiscovery(segment);
return {
segment,
leads: [
{ segment: 'mcp', target: 'MCP servers with paid/data-heavy tools', pitch: 'Add optional x402 payment gate + Pyrimid catalog listing.' },
{ segment: 'agent-frameworks', target: 'Agent frameworks with marketplace/plugin systems', pitch: 'Let builders sell tools to agents with Base USDC settlement.' },
{ segment: 'api-tools', target: 'AI API services with per-call cost', pitch: 'Turn API calls into agent-purchasable products.' },
...discovery,
scoring_model: {
base: 30,
mcp_signal: 25,
monetization_signal: 20,
agent_tool_signal: 15,
segment_match: 10,
popularity_cap: 10,
},
next_actions: [
'Open the repo and confirm it exposes a tool/API surface with repeat buyer value.',
'Pick one endpoint to wrap with an unpaid 402 challenge and a paid response path.',
'List product metadata in Pyrimid with price_usdc, affiliate_bps, endpoint, method, and output_schema.',
],
};
}
Expand Down Expand Up @@ -128,7 +259,7 @@ export async function GET(req: NextRequest, context: { params: Promise<{ product
payment_tx: verification.txHash,
payment_amount: verification.amount?.toString(),
buyer: verification.buyer,
...payload(product.product_id, req, proof),
...(await payload(product.product_id, req, proof)),
routed_by: 'pyrimid',
links: {
docs: 'https://pyrimid.ai/quickstart',
Expand Down