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
294 changes: 265 additions & 29 deletions app/api/v1/paid/[product]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,266 @@ function paymentRequired(req: NextRequest, product: NonNullable<ReturnType<typeo
);
}

function payload(productId: string, req: NextRequest, proof: string) {
type VendorLead = {
segment: string;
target: string;
url: string;
score: number;
fit_reasons: string[];
suggested_product: string;
pitch: string;
};

const LEAD_SEGMENTS: Record<string, { query: string; product: string; pitch: string; seeds: VendorLead[] }> = {
mcp: {
query: 'mcp server tools marketplace ai api language:TypeScript OR language:Python',
product: 'Paid MCP tool endpoint',
pitch: 'Add optional x402 gating to one high-value MCP tool and list the product in the Pyrimid catalog.',
seeds: [
{
segment: 'mcp',
target: 'Open-source MCP servers with data-heavy tools',
url: 'https://github.com/search?q=mcp+server+tools+api&type=repositories',
score: 74,
fit_reasons: ['MCP-native surface', 'tool calls can map directly to per-call pricing'],
suggested_product: 'Premium enrich/search/export tool',
pitch: 'Gate the most compute- or data-intensive tool at $0.05-$0.25 per call with x402 metadata.',
},
],
},
'agent-frameworks': {
query: 'agent framework plugin marketplace tools TypeScript Python',
product: 'Default Pyrimid resolver integration',
pitch: 'Let framework users discover paid tools and earn affiliate revenue on routed purchases.',
seeds: [
{
segment: 'agent-frameworks',
target: 'Agent frameworks with plugin or tool registries',
url: 'https://github.com/search?q=agent+framework+plugin+marketplace&type=repositories',
score: 70,
fit_reasons: ['Existing plugin distribution model', 'affiliate commission aligns with framework ecosystem growth'],
suggested_product: 'Embedded paid-tool resolver',
pitch: 'Ship PyrimidResolver as an optional monetization layer for tool recommendations.',
},
],
},
'api-tools': {
query: 'AI API SaaS per call pricing OpenAPI',
product: 'Paid API endpoint listed in Pyrimid catalog',
pitch: 'Turn a valuable API route into an agent-purchasable x402 product with Base USDC settlement.',
seeds: [
{
segment: 'api-tools',
target: 'AI API wrappers with measurable per-call value',
url: 'https://github.com/search?q=AI+API+OpenAPI+pricing&type=repositories',
score: 68,
fit_reasons: ['API route already exists', 'per-call cost can be priced and verified'],
suggested_product: 'Single paid JSON API call',
pitch: 'Expose one endpoint through Pyrimid middleware and publish catalog metadata.',
},
],
},
x402: {
query: 'x402 payment API USDC Base agent',
product: 'Pyrimid catalog + affiliate split for existing x402 product',
pitch: 'List an existing x402 product in Pyrimid so agents can discover it and route affiliate purchases.',
seeds: [
{
segment: 'x402',
target: 'Existing x402 APIs without affiliate routing',
url: 'https://github.com/search?q=x402+USDC+API&type=repositories',
score: 76,
fit_reasons: ['Already uses x402', 'Pyrimid can add catalog discovery and affiliate splits'],
suggested_product: 'Catalog listing for existing paid route',
pitch: 'Keep the current 402 flow, add Pyrimid product metadata and commission routing.',
},
],
},
};

function clampLimit(value: string | undefined, fallback = 5) {
const parsed = Number.parseInt(value || '', 10);
if (!Number.isFinite(parsed)) return fallback;
return Math.min(10, Math.max(1, parsed));
}

function recencyScore(pushedAt: string | null | undefined) {
if (!pushedAt) return 0;
const ageDays = (Date.now() - Date.parse(pushedAt)) / 86_400_000;
if (ageDays < 30) return 18;
if (ageDays < 180) return 12;
if (ageDays < 365) return 6;
return 0;
}

async function githubRepositoryLeads(segment: string, query: string, limit: number): Promise<{ leads: VendorLead[]; error?: string }> {
const url = new URL('https://api.github.com/search/repositories');
url.searchParams.set('q', query);
url.searchParams.set('sort', 'updated');
url.searchParams.set('order', 'desc');
url.searchParams.set('per_page', String(limit));

try {
const response = await fetch(url, {
headers: {
Accept: 'application/vnd.github+json',
'User-Agent': 'pyrimid-vendor-lead-discovery',
},
next: { revalidate: 900 },
});
if (!response.ok) return { leads: [], error: `github_search_${response.status}` };
const data = await response.json();
const items = Array.isArray(data.items) ? data.items : [];
return {
leads: items.slice(0, limit).map((repo: any): VendorLead => {
const stars = Number(repo.stargazers_count || 0);
const topics = Array.isArray(repo.topics) ? repo.topics : [];
const score = Math.min(
95,
45 + Math.min(20, Math.log10(stars + 1) * 8) + recencyScore(repo.pushed_at) + Math.min(12, topics.length * 2)
);
return {
segment,
target: repo.full_name || repo.name || 'unknown-repo',
url: repo.html_url,
score: Math.round(score),
fit_reasons: [
`${stars} GitHub stars`,
repo.pushed_at ? `updated ${repo.pushed_at.slice(0, 10)}` : 'unknown update date',
topics.length ? `topics: ${topics.slice(0, 4).join(', ')}` : 'topic metadata unavailable',
],
suggested_product: LEAD_SEGMENTS[segment]?.product || 'Paid API/MCP endpoint',
pitch: LEAD_SEGMENTS[segment]?.pitch || 'Add x402 payment metadata and Pyrimid catalog listing.',
};
}),
};
} catch (error) {
return { leads: [], error: error instanceof Error ? error.message : 'github_search_failed' };
}
}

async function vendorLeadDiscovery(query: Record<string, string>) {
const segment = (query.segment || 'mcp').toLowerCase();
const config = LEAD_SEGMENTS[segment] || LEAD_SEGMENTS.mcp;
const limit = clampLimit(query.limit);
const searchQuery = query.q || config.query;
const live = await githubRepositoryLeads(segment, searchQuery, limit);
const leads = [...live.leads, ...config.seeds]
.sort((a, b) => b.score - a.score)
.slice(0, limit);

return {
segment,
search_query: searchQuery,
generated_at: new Date().toISOString(),
leads,
lead_schema: {
target: 'repo, vendor, or ecosystem surface to contact',
score: '0-100 fit score from recency, public traction, and metadata richness',
fit_reasons: 'evidence an agent can verify before outreach',
suggested_product: 'first paid product shape to list in Pyrimid',
},
outreach_checklist: [
'Confirm the project exposes an MCP server, API route, or tool marketplace',
'Pick one high-value endpoint/tool with clear per-call value',
'Return 402 with x402 accepts[] metadata before payment',
'Publish Pyrimid catalog metadata: vendorId, productId, endpoint, price, affiliateBps, output_schema',
'Run one non-self buyer transaction and keep the Base tx hash as proof',
],
fallback_used: live.leads.length === 0,
source_errors: live.error ? [live.error] : [],
};
}

async function fetchTextSample(url: string) {
try {
const response = await fetch(url, {
headers: { Accept: 'application/json, text/plain, text/html;q=0.7, */*;q=0.2' },
next: { revalidate: 600 },
});
const text = await response.text();
return {
ok: response.ok,
status: response.status,
content_type: response.headers.get('content-type'),
sample: text.slice(0, 8000),
};
} catch (error) {
return {
ok: false,
status: 0,
content_type: null,
sample: '',
error: error instanceof Error ? error.message : 'fetch_failed',
};
}
}

function keywordEvidence(sample: string) {
const lower = sample.toLowerCase();
return {
has_mcp: /\bmcp\b|tools\/list|json-rpc/.test(lower),
has_tool_schema: /inputschema|tool(s)?\s*[:=]|function/.test(lower),
has_pricing: /price|pricing|usd|usdc|billing|credit/.test(lower),
has_x402: /x402|402 payment|payment-required|x-payment/.test(lower),
has_auth: /api[_-]?key|bearer|authorization|oauth/.test(lower),
};
}

async function mcpServerAudit(query: Record<string, string>) {
const url = query.url || 'https://example.com/mcp';
const fetched = await fetchTextSample(url);
const evidence = keywordEvidence(fetched.sample || '');
const observations = [
evidence.has_mcp ? 'MCP/JSON-RPC signals found in fetched content.' : 'No strong MCP marker found in fetched sample; verify server endpoint manually.',
evidence.has_tool_schema ? 'Tool/schema language present; individual tools can be priced separately.' : 'Tool schema not obvious from sample; publish tools/list or server card metadata.',
evidence.has_x402 ? 'x402/payment-required markers already present.' : 'No x402 marker detected; add 402 response with accepts[] metadata.',
evidence.has_pricing ? 'Pricing/billing language detected.' : 'No public pricing detected; start with one low-friction $0.01-$0.25 tool.',
];

const recommendedTools = evidence.has_tool_schema
? ['premium_search', 'bulk_enrich', 'export_report', 'deep_analyze']
: ['status_check', 'catalog_lookup', 'summary_report', 'data_export'];

return {
audit: {
url,
fetched: {
ok: fetched.ok,
status: fetched.status,
content_type: fetched.content_type,
sample_chars: fetched.sample.length,
error: fetched.error,
},
evidence,
observations,
recommended_paid_tools: recommendedTools.map((name, idx) => ({
name,
suggested_price_usdc: [0.01, 0.05, 0.1, 0.25][idx],
why: idx < 2 ? 'low-cost discovery/conversion tool' : 'higher-value compute or export action',
})),
x402_route_shape: {
unpaid: 'Return HTTP 402 with accepts[] including network=eip155:8453, asset=Base USDC, amount, payTo, and resource URL.',
paid: 'Verify X-PAYMENT or X-PAYMENT-TX on Base, then return JSON with product_id, payment_tx, buyer, and result.',
},
catalog_metadata: ['vendorId', 'productId', 'name', 'description', 'endpoint', 'method', 'price_usdc', 'affiliateBps', 'output_schema'],
risk_notes: [
'Do not put secrets in MCP tool descriptions or public catalog metadata.',
'Avoid pricing tools that only wrap free public data unless the transformation adds clear value.',
'Keep one free health/catalog endpoint so agents can discover the paid route before buying.',
],
integration_steps: [
'Choose one high-value tool and define a stable JSON output schema.',
'Add an unpaid 402 branch with x402 accepts[] metadata.',
'Add payment verification for Base USDC and reject stale or wrong-amount proofs.',
'Register the product in the Pyrimid catalog with affiliateBps for distribution agents.',
'Run one non-self test purchase and keep the Base transaction hash.',
],
},
};
}

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

switch (productId) {
Expand All @@ -51,33 +310,10 @@ function payload(productId: string, req: NextRequest, proof: string) {
],
};
}
case 'vendor-lead-discovery': {
const segment = query.segment || 'mcp';
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.' },
],
};
}
case 'mcp-server-audit': {
const url = query.url || 'https://example.com/mcp';
return {
audit: {
url,
recommended_paid_tools: ['search', 'enrich', 'export', 'analyze'],
pricing: '$0.01-$0.25 per call depending on compute/data cost',
integration_steps: [
'Add 402 response with x402 accepts[] metadata',
'Register vendor/product in Pyrimid catalog',
'Expose tool schema in MCP server card',
'Add affiliateBps for distribution agents',
],
},
};
}
case 'vendor-lead-discovery':
return vendorLeadDiscovery(query);
case 'mcp-server-audit':
return mcpServerAudit(query);
case 'x402-integration-plan': {
const service = query.service || 'agent-api';
return {
Expand Down Expand Up @@ -128,7 +364,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