diff --git a/src/data/affiliates.json b/src/data/affiliates.json
new file mode 100644
index 0000000..ca5682f
--- /dev/null
+++ b/src/data/affiliates.json
@@ -0,0 +1,115 @@
+{
+ "version": "1.0.0",
+ "updatedAt": "2026-11-10T10:36:23.696Z",
+ "description": "DevShelf Verified Developer Affiliate & Partner Perks Registry. Cleanly maps high-converting developer perks, free cloud credits, and verified partner links to directory dossiers without tracking param pollution.",
+ "partners": [
+ {
+ "slug": "digitalocean",
+ "toolName": "DigitalOcean",
+ "url": "https://www.awin1.com/awclick.php?mid=123996&id=3132128",
+ "vanityPath": "/go/digitalocean.html",
+ "category": "cloud",
+ "perkTitle": "$200 Free Cloud Credits (60 Days)",
+ "perkDescription": "Get $200 in free cloud infrastructure credits to deploy Droplets, Kubernetes, and App Platform instances.",
+ "ctaText": "Claim $200 Credits →",
+ "badge": "🎁 $200 Dev Credit",
+ "active": true
+ },
+ {
+ "slug": "hetzner",
+ "toolName": "Hetzner Cloud",
+ "url": "https://hetzner.cloud/?ref=devshelf",
+ "vanityPath": "/go/hetzner.html",
+ "category": "cloud",
+ "perkTitle": "€20 Free Cloud Credits",
+ "perkDescription": "High-performance European and US cloud instances starting at €3.79/mo. Claim €20 in free test credits.",
+ "ctaText": "Claim €20 Credits →",
+ "badge": "🎁 €20 Cloud Credit",
+ "active": true
+ },
+ {
+ "slug": "vultr",
+ "toolName": "Vultr",
+ "url": "https://www.vultr.com/?ref=9930418",
+ "vanityPath": "/go/vultr.html",
+ "category": "cloud",
+ "perkTitle": "$100 Free Cloud Credits",
+ "perkDescription": "Global cloud compute, bare metal, and managed Kubernetes across 32 worldwide locations with instant credits.",
+ "ctaText": "Claim $100 Credits →",
+ "badge": "🎁 $100 Cloud Credit",
+ "active": true
+ },
+ {
+ "slug": "geekflare",
+ "toolName": "Geekflare",
+ "url": "https://www.awin1.com/awclick.php?mid=124982&id=3132128",
+ "vanityPath": "/go/geekflare.html",
+ "category": "ai",
+ "perkTitle": "Developer AI APIs & Web Data Extraction",
+ "perkDescription": "High-speed developer APIs for scraping, data extraction, and collaborative AI models.",
+ "ctaText": "Explore APIs →",
+ "badge": "⚡ Dev API Perks",
+ "active": true
+ },
+ {
+ "slug": "supabase",
+ "toolName": "Supabase",
+ "url": "https://supabase.com/?ref=devshelf",
+ "vanityPath": "/go/supabase.html",
+ "category": "cloud",
+ "perkTitle": "Generous Permanent Free Postgres Tier",
+ "perkDescription": "Full Postgres database, authentication, instant REST/GraphQL APIs, and real-time subscriptions with 500MB storage.",
+ "ctaText": "Launch Free Project →",
+ "badge": "⚡ Verified Free Tier",
+ "active": true
+ },
+ {
+ "slug": "neon",
+ "toolName": "Neon",
+ "url": "https://neon.tech/?ref=devshelf",
+ "vanityPath": "/go/neon.html",
+ "category": "cloud",
+ "perkTitle": "Serverless Postgres with Instant Branching",
+ "perkDescription": "Autoscaling, database branching like Git, and zero cold starts with generous free tier allowances.",
+ "ctaText": "Get Free Postgres →",
+ "badge": "⚡ Free Serverless DB",
+ "active": true
+ },
+ {
+ "slug": "railway",
+ "toolName": "Railway",
+ "url": "https://railway.com/?ref=devshelf",
+ "vanityPath": "/go/railway.html",
+ "category": "cloud",
+ "perkTitle": "$5 Monthly Free Cloud & API Hosting",
+ "perkDescription": "Instant git-push deployments for Node.js, Python, Docker, MCP servers, and Postgres.",
+ "ctaText": "Deploy on Railway ->",
+ "badge": "⚡ Cloud Free Tier",
+ "active": true
+ },
+ {
+ "slug": "posthog",
+ "toolName": "PostHog",
+ "url": "https://posthog.com/?ref=devshelf",
+ "vanityPath": "/go/posthog.html",
+ "category": "testing",
+ "perkTitle": "1M Free Events + 5,000 Session Replays/mo",
+ "perkDescription": "Open-source product analytics, feature flags, session replay, and LLM observability.",
+ "ctaText": "Start Free on PostHog ->",
+ "badge": "⚡ 1M Free Events",
+ "active": true
+ },
+ {
+ "slug": "cloudflare",
+ "toolName": "Cloudflare",
+ "url": "https://www.cloudflare.com/?ref=devshelf",
+ "vanityPath": "/go/cloudflare.html",
+ "category": "cloud",
+ "perkTitle": "100,000 Free Daily Worker Invocations + Pages",
+ "perkDescription": "Global edge workers, zero-egress R2 storage, D1 SQLite, and unlimited static sites.",
+ "ctaText": "Launch on Cloudflare ->",
+ "badge": "⚡ Free Edge Workers",
+ "active": true
+ }
+ ]
+}
diff --git a/src/data/devshelf.json b/src/data/devshelf.json
index 70e07b7..3024bb6 100644
--- a/src/data/devshelf.json
+++ b/src/data/devshelf.json
@@ -1,5 +1,5 @@
{
- "updatedAt": "2026-11-07T18:08:29.138Z",
+ "updatedAt": "2026-11-10T11:17:51.960Z",
"totalCount": 575,
"reposCount": 363,
"facets": {
@@ -31,9 +31,9 @@
"100% Offline-Friendly",
"No Card Required"
],
+ "pypi": "https://pypi.org/project/promptshield-defense/",
"type": "ai",
- "section": "AI Agents & Local LLMs",
- "pypi": "https://pypi.org/project/promptshield-defense/"
+ "section": "AI Agents & Local LLMs"
},
{
"name": "AgentShield",
@@ -50,9 +50,9 @@
"100% Offline-Friendly",
"No Card Required"
],
+ "pypi": "https://pypi.org/project/agentshield-defense/",
"type": "ai",
- "section": "AI Agents & Local LLMs",
- "pypi": "https://pypi.org/project/agentshield-defense/"
+ "section": "AI Agents & Local LLMs"
},
{
"name": "mcp-monetize",
@@ -66,9 +66,9 @@
"No Card Required",
"Featured"
],
+ "pypi": "https://pypi.org/project/mcp-monetize/",
"type": "ai",
- "section": "AI Agents & Local LLMs",
- "pypi": "https://pypi.org/project/mcp-monetize/"
+ "section": "AI Agents & Local LLMs"
},
{
"name": "HOL Guard",
@@ -1570,9 +1570,9 @@
"language": "TypeScript",
"license": "MIT",
"featured": true,
+ "pypi": "https://pypi.org/project/dbshield-defense/",
"type": "ai",
- "section": "AI Agents & Local LLMs",
- "pypi": "https://pypi.org/project/dbshield-defense/"
+ "section": "AI Agents & Local LLMs"
},
{
"name": "Pydantic AI",
@@ -2524,6 +2524,7 @@
"100% Offline-Friendly",
"No Card Required"
],
+ "status": "Needs Maintenance",
"type": "cli",
"section": "CLI & Productivity Tools",
"alternativeTo": "Disk Inventory X / DaisyDisk"
@@ -3493,6 +3494,7 @@
"language": "Python",
"license": "MIT",
"featured": false,
+ "status": "Needs Maintenance",
"type": "cli",
"section": "CLI & Productivity Tools"
},
@@ -3653,6 +3655,13 @@
"language": "Claude Code plugin, Markdown skills and agents, Bash hooks, JSON manifests",
"license": "MIT",
"featured": false,
+ "installNote": "To install the plugin, open Claude Code and run these as two separate commands:",
+ "installCommands": [
+ "/plugin marketplace add https://github.com/akii-technologies-ltd/akii-seo-ai-search-optimizer",
+ "/plugin install akii-seo-ai-search-optimizer@akii"
+ ],
+ "installDocsUrl": "https://github.com/akii-technologies-ltd/akii-seo-ai-search-optimizer#installation",
+ "installDocsLabel": "Official installation instructions",
"type": "cli",
"section": "CLI & Productivity Tools"
},
@@ -5012,6 +5021,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5140,6 +5150,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5156,6 +5167,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5252,6 +5264,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5332,6 +5345,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5348,6 +5362,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5380,6 +5395,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5540,6 +5556,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5684,6 +5701,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5828,6 +5846,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5844,6 +5863,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5860,6 +5880,7 @@
"Rate-Limited",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -5876,6 +5897,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -6124,6 +6146,7 @@
"cors": "Yes",
"rateLimit": "No numerical rate limit is published. Static, read-only JSON with no account, API key or card; consumers should cache responses.",
"https": true,
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -6136,6 +6159,7 @@
"cors": "Yes",
"rateLimit": "No fixed quota, fair use. Every endpoint is free and keyless.",
"https": true,
+ "status": "Needs Maintenance",
"type": "api",
"section": "Free & Public APIs"
},
@@ -6450,6 +6474,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "cloud",
"section": "Free Cloud & Developer Tiers"
},
@@ -6636,6 +6661,7 @@
"Requires Credit Card to Verify",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "cloud",
"section": "Free Cloud & Developer Tiers",
"alternativeTo": "Heroku"
@@ -6766,6 +6792,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "cloud",
"section": "Free Cloud & Developer Tiers"
},
@@ -7118,6 +7145,7 @@
"Requires Credit Card to Verify",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "cloud",
"section": "Free Cloud & Developer Tiers"
},
@@ -7131,6 +7159,7 @@
"No Card Required",
"CI Verified"
],
+ "status": "Needs Maintenance",
"type": "cloud",
"section": "Free Cloud & Developer Tiers",
"alternativeTo": "Google Analytics",
@@ -7350,6 +7379,7 @@
"description": "Free enterprise-grade CDN, DDoS protection, edge workers, and SSL certificate management for open-source initiatives.",
"perkValue": "Free Enterprise Plan features & Edge bandwidth",
"eligibility": "Non-profit or community-driven open-source projects with OSI-approved licenses",
+ "status": "Needs Maintenance",
"type": "perks",
"section": "Developer Discounts & Startup Perks"
},
@@ -7360,6 +7390,7 @@
"description": "Free 1Password Teams accounts for open-source project maintainers to securely share secrets and API tokens.",
"perkValue": "Free 1Password Teams subscription",
"eligibility": "Active open-source project maintainers with public GitHub repositories",
+ "status": "Needs Maintenance",
"type": "perks",
"section": "Developer Discounts & Startup Perks"
},
@@ -7432,6 +7463,7 @@
"description": "Up to $500 in Twilio communication API credits, sendgrid volume, and technical mentorship for early-stage software companies.",
"perkValue": "Up to $500 in Twilio credits + SendGrid boosts",
"eligibility": "Early-stage startups building customer communications",
+ "status": "Needs Maintenance",
"type": "perks",
"section": "Developer Discounts & Startup Perks"
},
@@ -7472,6 +7504,7 @@
"description": "Free localization and translation management platform for open-source software projects and global documentation.",
"perkValue": "Free Crowdin Enterprise translation suite",
"eligibility": "Non-commercial open-source projects with public repositories",
+ "status": "Needs Maintenance",
"type": "perks",
"section": "Developer Discounts & Startup Perks"
},
diff --git a/src/lib/affiliates.ts b/src/lib/affiliates.ts
new file mode 100644
index 0000000..b4288fe
--- /dev/null
+++ b/src/lib/affiliates.ts
@@ -0,0 +1,68 @@
+import affiliatesData from "@/data/affiliates.json";
+
+export interface AffiliatePartner {
+ slug: string;
+ toolName: string;
+ url: string;
+ vanityPath: string;
+ category?: string;
+ perkTitle: string;
+ perkDescription: string;
+ ctaText?: string;
+ badge?: string;
+ active: boolean;
+}
+
+function getHostname(urlStr: string): string {
+ try {
+ return new URL(urlStr).hostname.replace(/^www\./, "").toLowerCase();
+ } catch {
+ return "";
+ }
+}
+
+function normalizeSlug(str: string): string {
+ return str
+ .toLowerCase()
+ .trim()
+ .replace(/[^a-z0-9]+/g, "-")
+ .replace(/^-|-$/g, "");
+}
+
+export function getActivePartners(): AffiliatePartner[] {
+ const partners = (affiliatesData.partners as AffiliatePartner[]) || [];
+ return partners.filter((p) => p.active);
+}
+
+export function getPartnerBySlug(slug: string): AffiliatePartner | undefined {
+ return getActivePartners().find((p) => p.slug.toLowerCase() === slug.toLowerCase());
+}
+
+export function getCloudHostingPartners(): AffiliatePartner[] {
+ const prioritySlugs = ["digitalocean", "vultr", "hetzner", "railway"];
+ const active = getActivePartners();
+ return prioritySlugs
+ .map((s) => active.find((p) => p.slug === s))
+ .filter((p): p is AffiliatePartner => Boolean(p));
+}
+
+export function findPartnerForTool(tool: {
+ name?: string;
+ url?: string;
+ repo?: string;
+}): AffiliatePartner | undefined {
+ if (!tool?.name) return undefined;
+ const nameLower = tool.name.toLowerCase();
+ const toolSlug = normalizeSlug(tool.name);
+ const toolHost = getHostname(tool.url || tool.repo || "");
+
+ return getActivePartners().find((p) => {
+ const pSlug = p.slug.toLowerCase();
+ const pName = p.toolName.toLowerCase();
+ if (pSlug === nameLower || pName === nameLower || toolSlug === pSlug) return true;
+ if (nameLower.includes(pName)) return true;
+ const partnerHost = getHostname(p.url);
+ if (toolHost && partnerHost && toolHost === partnerHost) return true;
+ return false;
+ });
+}
diff --git a/src/lib/cf-webhook.d.ts b/src/lib/cf-webhook.d.ts
new file mode 100644
index 0000000..f8ba985
--- /dev/null
+++ b/src/lib/cf-webhook.d.ts
@@ -0,0 +1,6 @@
+export function dispatchOrderWebhook(
+ env: Record | NodeJS.ProcessEnv,
+ messageText: string
+): Promise;
+
+export function jsonResponse(payload: unknown, status?: number): Response;
diff --git a/src/lib/cf-webhook.js b/src/lib/cf-webhook.js
new file mode 100644
index 0000000..65a878d
--- /dev/null
+++ b/src/lib/cf-webhook.js
@@ -0,0 +1,50 @@
+/**
+ * Shared webhook dispatcher compatible with Slack, Discord, Microsoft Teams, and Zapier.
+ * Runs in both Node runtimes (process.env) and Cloudflare Pages Workers (context.env).
+ */
+export async function dispatchOrderWebhook(env, messageText) {
+ const webhookUrl =
+ env?.ENTERPRISE_WEBHOOK_URL ||
+ env?.SLACK_WEBHOOK_URL ||
+ env?.DISCORD_WEBHOOK_URL;
+ if (!webhookUrl) return false;
+
+ try {
+ const res = await fetch(webhookUrl, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ text: messageText,
+ content: messageText,
+ }),
+ });
+ return res.ok;
+ } catch (err) {
+ console.warn("Webhook dispatch warning:", err);
+ return false;
+ }
+}
+
+const CORS_HEADERS = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
+ "Access-Control-Allow-Headers": "Content-Type",
+};
+
+export function corsPreflightResponse() {
+ return new Response(null, {
+ status: 204,
+ headers: CORS_HEADERS,
+ });
+}
+
+export function jsonResponse(payload, status = 200) {
+ return new Response(JSON.stringify(payload), {
+ status,
+ headers: {
+ "Content-Type": "application/json",
+ "Cache-Control": "no-store",
+ ...CORS_HEADERS,
+ },
+ });
+}
diff --git a/src/lib/checkout-urls.d.ts b/src/lib/checkout-urls.d.ts
new file mode 100644
index 0000000..6c31ead
--- /dev/null
+++ b/src/lib/checkout-urls.d.ts
@@ -0,0 +1,64 @@
+export interface AuditCheckoutInput {
+ orderId: string;
+ packageTier?: string;
+ productName?: string;
+ targetUrl?: string;
+ repoUrl?: string;
+ contactEmail?: string;
+}
+
+export interface McpCheckoutInput {
+ orderId: string;
+ tier?: string;
+ companyName?: string;
+ apiDocsUrl?: string;
+ contactEmail?: string;
+ preferredTransport?: string;
+ includeMonetization?: boolean;
+}
+
+export type PartnerTierId =
+ | "starter_9"
+ | "spotlight_19"
+ | "category_39"
+ | "leader_49"
+ | "combo_79"
+ | "custom";
+
+export interface PartnerCheckoutInput {
+ orderId: string;
+ tier?: PartnerTierId | string;
+ toolName?: string;
+ toolUrl?: string;
+ category?: string;
+ bundleCategories?: string[];
+ customAmountUsd?: number | string;
+ sponsorHandle?: string;
+ contactEmail?: string;
+}
+
+export interface PreparedPartnerOrder {
+ status: number;
+ error?: string;
+ orderId?: string;
+ tier?: string;
+ amountUsd?: string;
+ checkoutUrl?: string;
+ summary?: string;
+}
+
+export interface CloudOrPrivateCheckoutInput {
+ tier?: "team_relay_29" | "team_hub_99" | "private_compliance_kit";
+ customerEmail?: string;
+ githubUsername?: string;
+}
+
+export function resolvePartnerTierPricing(
+ tier?: string,
+ customAmountUsd?: number | string
+): { tierId: string; amountUsd: string; label: string };
+export function preparePartnerOrder(body: unknown): PreparedPartnerOrder;
+export function buildAuditCheckoutUrl(input: AuditCheckoutInput): string;
+export function buildMcpCheckoutUrl(input: McpCheckoutInput): string;
+export function buildPartnerCheckoutUrl(input: PartnerCheckoutInput): string;
+export function buildCloudOrPrivateCheckoutUrl(input: CloudOrPrivateCheckoutInput): string;
diff --git a/src/lib/checkout-urls.js b/src/lib/checkout-urls.js
new file mode 100644
index 0000000..f3aea87
--- /dev/null
+++ b/src/lib/checkout-urls.js
@@ -0,0 +1,283 @@
+/**
+ * Shared Dodo Payments checkout URL builders used by both client pages (fallback)
+ * and Cloudflare Pages / Next.js API handlers so pricing, tiers, and metadata
+ * never drift out of sync.
+ */
+
+const DODO_AUDIT_STARTER_199 = "pdt_0NrFWnRHHEI72iaB8AC9r";
+const DODO_AUDIT_PRO_399 = "pdt_0NrFWnSQHfJhuHs4Y9Xmh";
+const DODO_SERVICE_FLEX = "pdt_0NpjONGugY5QkrFDUaPva";
+
+export function buildAuditCheckoutUrl({
+ orderId,
+ packageTier = "starter_199",
+ productName = "",
+ targetUrl = "",
+ repoUrl = "",
+ contactEmail = "",
+}) {
+ const isPro = packageTier === "pro_399" || packageTier === "comprehensive_7500" || packageTier === "rapid_1500";
+ const productId = packageTier === "pro_399" ? DODO_AUDIT_PRO_399 : DODO_AUDIT_STARTER_199;
+ const params = new URLSearchParams();
+ params.set("quantity", "1");
+ if (contactEmail) {
+ params.set("email", contactEmail.trim().toLowerCase());
+ params.set("customer_email", contactEmail.trim().toLowerCase());
+ }
+ if (productName) {
+ params.set("fullName", productName.trim());
+ params.set("metadata_product", productName.trim().slice(0, 120));
+ }
+ params.set("metadata_orderId", orderId);
+ params.set("metadata_tier", isPro && packageTier !== "rapid_1500" ? packageTier : "starter_199");
+ if (targetUrl) params.set("metadata_targetUrl", targetUrl.trim().slice(0, 200));
+ if (repoUrl) params.set("metadata_repoUrl", repoUrl.trim().slice(0, 200));
+ params.set(
+ "redirect_url",
+ `https://ritualdev.in/audit/success/?id=${encodeURIComponent(orderId)}&tier=${encodeURIComponent(packageTier)}`
+ );
+ return `https://checkout.dodopayments.com/buy/${productId}?${params.toString()}`;
+}
+
+export function buildMcpCheckoutUrl({
+ orderId,
+ tier = "standard_349",
+ companyName = "",
+ apiDocsUrl = "",
+ contactEmail = "",
+ preferredTransport = "stdio + sse",
+ includeMonetization = true,
+}) {
+ const amountUsd = tier === "pro_699" ? "699" : "349";
+ const params = new URLSearchParams();
+ params.set("quantity", "1");
+ params.set("paymentAmount", amountUsd);
+ params.set("amount", amountUsd);
+ params.set("paymentCurrency", "USD");
+ params.set("currency", "USD");
+ params.set("singlePage", "true");
+ params.set("product_type", "digital");
+ params.set("shipping_required", "false");
+ if (contactEmail) {
+ params.set("email", contactEmail.trim().toLowerCase());
+ params.set("customer_email", contactEmail.trim().toLowerCase());
+ }
+ if (companyName) {
+ params.set("fullName", companyName.trim());
+ params.set("metadata_company", companyName.trim().slice(0, 120));
+ }
+ params.set("metadata_orderId", orderId);
+ params.set("metadata_tier", tier);
+ params.set("metadata_transport", preferredTransport);
+ params.set("metadata_monetize", String(Boolean(includeMonetization)));
+ if (apiDocsUrl) params.set("metadata_apiDocsUrl", apiDocsUrl.trim().slice(0, 200));
+ params.set(
+ "redirect_url",
+ `https://ritualdev.in/mcp-builder/?success=true&id=${encodeURIComponent(orderId)}&tier=${encodeURIComponent(tier)}`
+ );
+ return `https://checkout.dodopayments.com/buy/${DODO_SERVICE_FLEX}?${params.toString()}`;
+}
+
+export const PARTNER_TIER_CONFIG = {
+ starter_9: {
+ id: "starter_9",
+ amountUsd: 9,
+ period: "one-time",
+ label: "$9 (Starter Verified Listing)",
+ },
+ spotlight_19: {
+ id: "spotlight_19",
+ amountUsd: 19,
+ period: "30-day boost",
+ label: "$19 (Launch Spotlight + Custom FAQs & Digest)",
+ },
+ category_39: {
+ id: "category_39",
+ amountUsd: 39,
+ period: "per month",
+ label: "$39/mo (Category Exclusive Spotlight)",
+ },
+ leader_49: {
+ id: "leader_49",
+ amountUsd: 49,
+ period: "per month",
+ label: "$49/mo (MCP Pro + Category Leader)",
+ },
+ combo_79: {
+ id: "combo_79",
+ amountUsd: 79,
+ period: "per month",
+ label: "$79/mo (Category Combo Pack + Homepage Takeover)",
+ },
+ custom: {
+ id: "custom",
+ amountUsd: 25,
+ period: "custom sponsor",
+ label: "Custom Sponsor / Pay-What-You-Want",
+ },
+};
+
+export function resolvePartnerTierPricing(tier = "spotlight_19", customAmountUsd) {
+ const cfg = PARTNER_TIER_CONFIG[tier] || PARTNER_TIER_CONFIG.spotlight_19;
+ if (cfg.id !== "custom") {
+ return {
+ tierId: cfg.id,
+ amountUsd: String(cfg.amountUsd),
+ label: cfg.label,
+ };
+ }
+ const parsed = Number(customAmountUsd);
+ const clamped = Number.isFinite(parsed) && parsed > 0 ? Math.max(5, Math.min(5000, Math.round(parsed))) : 25;
+ return {
+ tierId: "custom",
+ amountUsd: String(clamped),
+ label: `$${clamped} (Custom Sponsor / Bespoke Pack)`,
+ };
+}
+
+export function buildPartnerCheckoutUrl({
+ orderId,
+ tier = "spotlight_19",
+ toolName = "",
+ toolUrl = "",
+ category = "ai",
+ bundleCategories = [],
+ customAmountUsd,
+ sponsorHandle = "",
+ contactEmail = "",
+}) {
+ const pricing = resolvePartnerTierPricing(tier, customAmountUsd);
+ const params = new URLSearchParams();
+ params.set("quantity", "1");
+ params.set("paymentAmount", pricing.amountUsd);
+ params.set("amount", pricing.amountUsd);
+ params.set("paymentCurrency", "USD");
+ params.set("currency", "USD");
+ params.set("singlePage", "true");
+ params.set("product_type", "digital");
+ params.set("shipping_required", "false");
+ if (contactEmail) {
+ params.set("email", contactEmail.trim().toLowerCase());
+ params.set("customer_email", contactEmail.trim().toLowerCase());
+ }
+ const displayName = (toolName || sponsorHandle || "Custom DevShelf Sponsor").trim();
+ if (displayName) {
+ params.set("fullName", displayName);
+ params.set("metadata_toolName", displayName.slice(0, 120));
+ }
+ if (toolUrl) params.set("metadata_toolUrl", toolUrl.trim().slice(0, 200));
+ if (sponsorHandle) params.set("metadata_sponsorHandle", sponsorHandle.trim().replace(/^@/, "").slice(0, 60));
+ if (Array.isArray(bundleCategories) && bundleCategories.length > 0) {
+ params.set("metadata_bundleCategories", bundleCategories.slice(0, 3).join(","));
+ }
+ params.set("metadata_orderId", orderId);
+ params.set("metadata_tier", pricing.tierId);
+ params.set("metadata_category", category);
+ params.set(
+ "redirect_url",
+ `https://ritualdev.in/partners/?success=true&id=${encodeURIComponent(orderId)}&tier=${encodeURIComponent(pricing.tierId)}&amount=${encodeURIComponent(pricing.amountUsd)}`
+ );
+ return `https://checkout.dodopayments.com/buy/${DODO_SERVICE_FLEX}?${params.toString()}`;
+}
+
+export function preparePartnerOrder(body) {
+ const {
+ toolName = "",
+ toolUrl = "",
+ category = "ai",
+ bundleCategories = [],
+ contactEmail = "",
+ tier = "spotlight_19",
+ customAmountUsd,
+ sponsorHandle = "",
+ tagline = "",
+ orderId: clientOrderId,
+ } = body || {};
+
+ if (!contactEmail || typeof contactEmail !== "string" || !contactEmail.includes("@")) {
+ return { error: "A valid contact email is required.", status: 400 };
+ }
+
+ const isCustom = tier === "custom";
+ if (!isCustom && (!toolName || !toolUrl)) {
+ return { error: "Both Tool Name and Tool URL / Repo are required.", status: 400 };
+ }
+ if (isCustom && !toolName && !sponsorHandle) {
+ return { error: "Please enter your name, handle, or tool/company name for custom sponsorship.", status: 400 };
+ }
+
+ const orderId = clientOrderId || `SPONSOR-${Math.floor(10000 + Math.random() * 90000)}`;
+ const pricing = resolvePartnerTierPricing(tier, customAmountUsd);
+ const checkoutUrl = buildPartnerCheckoutUrl({
+ orderId,
+ tier: pricing.tierId,
+ toolName,
+ toolUrl,
+ category,
+ bundleCategories,
+ customAmountUsd,
+ sponsorHandle,
+ contactEmail,
+ });
+
+ const cleanCategories =
+ Array.isArray(bundleCategories) && bundleCategories.length > 0
+ ? bundleCategories.slice(0, 3).join(", ")
+ : category;
+
+ const summary = [
+ `⭐ **New DevShelf Partner Order [${orderId}]**`,
+ `• **Tier:** ${pricing.label}`,
+ `• **Entity / Tool:** ${toolName || sponsorHandle} ${toolUrl ? `(${toolUrl})` : ""}`.trim(),
+ `• **Category / Bundle:** ${cleanCategories}`,
+ `• **Email:** ${contactEmail.trim().toLowerCase()}`,
+ `• **Notes / Pitch:** ${tagline || "None provided"}`,
+ ].join("\n");
+
+ return {
+ status: 200,
+ orderId,
+ tier: pricing.tierId,
+ amountUsd: pricing.amountUsd,
+ checkoutUrl,
+ summary,
+ };
+}
+
+export function buildCloudOrPrivateCheckoutUrl({
+ tier = "team_relay_29",
+ customerEmail = "",
+ githubUsername = "",
+}) {
+ const amountMap = {
+ team_relay_29: "29",
+ team_hub_99: "99",
+ private_compliance_kit: "199",
+ };
+ const amountUsd = amountMap[tier] || "29";
+ const productId = tier === "private_compliance_kit" ? DODO_AUDIT_STARTER_199 : DODO_SERVICE_FLEX;
+ const params = new URLSearchParams();
+ params.set("quantity", "1");
+ if (productId === DODO_SERVICE_FLEX) {
+ params.set("paymentAmount", amountUsd);
+ params.set("amount", amountUsd);
+ params.set("paymentCurrency", "USD");
+ params.set("currency", "USD");
+ params.set("singlePage", "true");
+ params.set("product_type", "digital");
+ params.set("shipping_required", "false");
+ }
+ if (customerEmail) {
+ params.set("email", customerEmail.trim().toLowerCase());
+ params.set("customer_email", customerEmail.trim().toLowerCase());
+ }
+ if (githubUsername) {
+ params.set("metadata_github_username", githubUsername.trim().replace(/^@/, ""));
+ }
+ params.set("metadata_tier", tier);
+ params.set(
+ "redirect_url",
+ `https://ritualdev.in/pricing/success/?product=${encodeURIComponent(tier)}`
+ );
+ return `https://checkout.dodopayments.com/buy/${productId}?${params.toString()}`;
+}
diff --git a/src/lib/dodo-webhook.d.ts b/src/lib/dodo-webhook.d.ts
new file mode 100644
index 0000000..4279c07
--- /dev/null
+++ b/src/lib/dodo-webhook.d.ts
@@ -0,0 +1,39 @@
+export function verifyDodoWebhookSignature(params: {
+ rawBody: string;
+ webhookId: string;
+ webhookTimestamp: string;
+ webhookSignature: string;
+ secret: string;
+ nowMs?: number;
+}): Promise;
+
+export function inviteGitHubCollaborator(params: {
+ repoSlug: string;
+ username: string;
+ githubPat: string;
+}): Promise<{ invited: boolean; status?: number; username?: string; reason?: string }>;
+
+export function dispatchDevShelfSponsorActivation(params: {
+ toolName?: string;
+ toolUrl?: string;
+ category?: string;
+ tier?: string;
+ amount?: string | number;
+ githubHandle?: string;
+ patronName?: string;
+ comment?: string;
+ githubPat?: string;
+}): Promise<{ dispatched: boolean; workflowTier?: string; status?: number; reason?: string }>;
+
+export function handleDodoWebhookEvent(params: {
+ rawBody: string;
+ headers: Headers;
+ env: Record | NodeJS.ProcessEnv;
+}): Promise<{
+ ok: boolean;
+ status: number;
+ error?: string;
+ eventType?: string;
+ paymentId?: string;
+ fulfillmentNote?: string;
+}>;
diff --git a/src/lib/dodo-webhook.js b/src/lib/dodo-webhook.js
new file mode 100644
index 0000000..1aa7fca
--- /dev/null
+++ b/src/lib/dodo-webhook.js
@@ -0,0 +1,303 @@
+import { dispatchOrderWebhook } from "./cf-webhook.js";
+
+function decodeBase64(b64) {
+ const binStr = atob(b64);
+ const bytes = new Uint8Array(binStr.length);
+ for (let i = 0; i < binStr.length; i++) {
+ bytes[i] = binStr.charCodeAt(i);
+ }
+ return bytes;
+}
+
+function encodeBase64(buffer) {
+ const bytes = new Uint8Array(buffer);
+ let bin = "";
+ for (let i = 0; i < bytes.length; i++) {
+ bin += String.fromCharCode(bytes[i]);
+ }
+ return btoa(bin);
+}
+
+function timingSafeEqual(a, b) {
+ if (a.length !== b.length) return false;
+ let diff = 0;
+ for (let i = 0; i < a.length; i++) {
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
+ }
+ return diff === 0;
+}
+
+/**
+ * Verifies Standard Webhooks (Dodo Payments) HMAC-SHA256 signature.
+ */
+export async function verifyDodoWebhookSignature({
+ rawBody,
+ webhookId,
+ webhookTimestamp,
+ webhookSignature,
+ secret,
+ nowMs = Date.now(),
+}) {
+ if (!secret || !webhookId || !webhookTimestamp || !webhookSignature) {
+ return false;
+ }
+
+ const tsSec = Number.parseInt(webhookTimestamp, 10);
+ if (!Number.isFinite(tsSec) || Math.abs(nowMs / 1000 - tsSec) > 300) {
+ return false;
+ }
+
+ const cleanSecret = secret.startsWith("whsec_") ? secret.slice(6) : secret;
+ const keyBytes = decodeBase64(cleanSecret);
+ const cryptoKey = await crypto.subtle.importKey(
+ "raw",
+ keyBytes,
+ { name: "HMAC", hash: "SHA-256" },
+ false,
+ ["sign"]
+ );
+
+ const signedContent = new TextEncoder().encode(`${webhookId}.${webhookTimestamp}.${rawBody}`);
+ const sigBuf = await crypto.subtle.sign("HMAC", cryptoKey, signedContent);
+ const expectedB64 = encodeBase64(sigBuf);
+
+ const candidates = webhookSignature.trim().split(/\s+/);
+ for (const cand of candidates) {
+ const [version, sig] = cand.split(",");
+ if (version === "v1" && sig && timingSafeEqual(sig, expectedB64)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+export async function inviteGitHubCollaborator({ repoSlug, username, githubPat }) {
+ if (!repoSlug || !username || !githubPat) {
+ return { invited: false, reason: "Missing GITHUB_PAT, repoSlug, or github_username" };
+ }
+ const cleanUser = username.replace(/^@/, "").replace(/[^a-zA-Z0-9-]/g, "");
+ if (!cleanUser) {
+ return { invited: false, reason: "Invalid GitHub username" };
+ }
+
+ const res = await fetch(
+ `https://api.github.com/repos/${repoSlug}/collaborators/${encodeURIComponent(cleanUser)}`,
+ {
+ method: "PUT",
+ headers: {
+ Authorization: `Bearer ${githubPat}`,
+ Accept: "application/vnd.github+json",
+ "User-Agent": "RitualDev-Fulfillment/1.0",
+ "X-GitHub-Api-Version": "2022-11-28",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ permission: "pull" }),
+ }
+ );
+ return {
+ invited: res.status === 201 || res.status === 204,
+ status: res.status,
+ username: cleanUser,
+ };
+}
+
+const SPONSOR_TIER_MAP = new Set([
+ "starter_9",
+ "spotlight_19",
+ "category_39",
+ "leader_49",
+ "combo_79",
+ "custom",
+ "starter",
+ "mcpPro",
+ "categoryTakeover",
+ "featuredSponsor",
+ "enterprise",
+ "newsletterSpotlight",
+]);
+
+function mapToWorkflowTier(rawTier = "") {
+ const t = String(rawTier || "").toLowerCase();
+ if (t.includes("combo") || t.includes("enterprise") || t.includes("featured") || t === "custom") {
+ return "enterprise";
+ }
+ if (t.includes("leader") || t.includes("category") || t.includes("mcp")) {
+ return "mcpPro";
+ }
+ return "starter";
+}
+
+function mapToWorkflowCategory(rawCat = "") {
+ const c = String(rawCat || "").toLowerCase().trim();
+ if (c === "databases") return "cloud";
+ if (c === "devops") return "cli";
+ const allowed = new Set(["ai", "cli", "apis", "cloud", "testing", "boilerplates", "perks"]);
+ return allowed.has(c) ? c : "cli";
+}
+
+export async function dispatchDevShelfSponsorActivation({
+ toolName,
+ toolUrl,
+ category = "cli",
+ tier = "spotlight_19",
+ amount,
+ githubHandle = "",
+ patronName = "",
+ comment = "",
+ githubPat,
+}) {
+ if (!githubPat) {
+ return { dispatched: false, reason: "Missing GITHUB_PAT" };
+ }
+ if (!toolName || !toolUrl) {
+ return { dispatched: false, reason: "Missing toolName or toolUrl metadata" };
+ }
+
+ const workflowTier = mapToWorkflowTier(tier);
+ const workflowCategory = mapToWorkflowCategory(category);
+ const normalizedUsd =
+ typeof amount === "number"
+ ? String(amount >= 100 && Number.isInteger(amount) ? Math.round(amount / 100) : amount)
+ : amount
+ ? String(amount)
+ : "";
+
+ const res = await fetch(
+ "https://api.github.com/repos/RitualDev-Lab/DevShelf/actions/workflows/activate-sponsor.yml/dispatches",
+ {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${githubPat}`,
+ Accept: "application/vnd.github+json",
+ "User-Agent": "RitualDev-Fulfillment/1.0",
+ "X-GitHub-Api-Version": "2022-11-28",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ ref: "main",
+ inputs: {
+ tool_name: String(toolName).slice(0, 120),
+ tool_url: String(toolUrl).slice(0, 200),
+ category: workflowCategory,
+ tier: workflowTier,
+ pitch: String(comment || `${toolName} — Verified DevShelf Sponsor (${tier})`).slice(0, 160),
+ patron_name: String(patronName || toolName).slice(0, 120),
+ github_handle: String(githubHandle || "").replace(/^@/, "").slice(0, 60),
+ amount: normalizedUsd,
+ comment: String(comment || `Activated tier: ${tier}`).slice(0, 300),
+ },
+ }),
+ }
+ );
+
+ return {
+ dispatched: res.status === 204 || res.status === 200,
+ workflowTier,
+ status: res.status,
+ };
+}
+
+export async function handleDodoWebhookEvent({ rawBody, headers, env }) {
+ const secret = env?.DODO_WEBHOOK_SECRET;
+ if (!secret) {
+ return { ok: false, status: 500, error: "DODO_WEBHOOK_SECRET not configured" };
+ }
+
+ const webhookId = headers.get("webhook-id") || "";
+ const webhookTimestamp = headers.get("webhook-timestamp") || "";
+ const webhookSignature = headers.get("webhook-signature") || "";
+
+ const isValid = await verifyDodoWebhookSignature({
+ rawBody,
+ webhookId,
+ webhookTimestamp,
+ webhookSignature,
+ secret,
+ });
+
+ if (!isValid) {
+ return { ok: false, status: 401, error: "Invalid webhook signature" };
+ }
+
+ let event;
+ try {
+ event = JSON.parse(rawBody);
+ } catch {
+ return { ok: false, status: 400, error: "Malformed JSON webhook body" };
+ }
+
+ const eventType = event?.type || event?.event_type || "unknown";
+ const data = event?.data || {};
+ const metadata = data?.metadata || {};
+ const customerEmail = data?.customer?.email || metadata?.customer_email || "unknown";
+ const amount = data?.total_amount ?? data?.amount ?? "unknown";
+ const currency = data?.currency || "USD";
+ const paymentId = data?.payment_id || data?.subscription_id || webhookId;
+
+ const isPaid =
+ eventType === "payment.succeeded" ||
+ eventType === "subscription.active" ||
+ eventType === "subscription.renewed";
+
+ let fulfillmentNote = "Recorded verified event.";
+ if (isPaid) {
+ const ghUser =
+ metadata?.github_username ||
+ metadata?.githubHandle ||
+ metadata?.sponsorHandle ||
+ metadata?.github;
+ const tier = metadata?.tier || metadata?.package || metadata?.plan || "hosted_relay";
+ const toolName = metadata?.toolName || metadata?.tool_name;
+ const toolUrl = metadata?.toolUrl || metadata?.tool_url || metadata?.url;
+ const orderId = metadata?.orderId || "";
+ const isSponsorPayment = SPONSOR_TIER_MAP.has(tier) || orderId.startsWith("SPONSOR-") || Boolean(toolName && toolUrl);
+
+ if (isSponsorPayment) {
+ const activation = await dispatchDevShelfSponsorActivation({
+ toolName: toolName || metadata?.sponsorName || customerEmail,
+ toolUrl: toolUrl || "https://ritualdev.in/partners",
+ category: metadata?.category || metadata?.toolCategory || "cli",
+ tier,
+ amount,
+ githubHandle: ghUser,
+ patronName: metadata?.sponsorName || data?.customer?.name || toolName,
+ comment: metadata?.sponsor_comment || metadata?.comment || metadata?.toolPitch || "",
+ githubPat: env?.GITHUB_PAT,
+ });
+ fulfillmentNote = activation.dispatched
+ ? `Dispatched DevShelf activate-sponsor.yml workflow (tier: ${activation.workflowTier})`
+ : `DevShelf workflow dispatch pending manual review: ${activation.reason || `HTTP ${activation.status}`}`;
+ } else if (ghUser && env?.GITHUB_PAT && env?.PRIVATE_COMPLIANCE_REPO) {
+ const invite = await inviteGitHubCollaborator({
+ repoSlug: env.PRIVATE_COMPLIANCE_REPO,
+ username: ghUser,
+ githubPat: env.GITHUB_PAT,
+ });
+ fulfillmentNote = invite.invited
+ ? `GitHub collaborator invited (@${invite.username} -> ${env.PRIVATE_COMPLIANCE_REPO})`
+ : `GitHub invite skipped/failed: ${invite.reason || `HTTP ${invite.status}`}`;
+ } else if (tier.includes("relay") || tier.includes("team")) {
+ fulfillmentNote = `Provision Hosted Cloud Relay for ${customerEmail} (tier: ${tier})`;
+ }
+
+ await dispatchOrderWebhook(
+ env,
+ [
+ `💰 **Verified Dodo Payment [${eventType}]**`,
+ `• **Payment ID:** ${paymentId}`,
+ `• **Customer:** ${customerEmail}`,
+ `• **Amount:** ${amount} ${currency}`,
+ `• **Tier / Metadata:** ${JSON.stringify(metadata)}`,
+ `• **Fulfillment:** ${fulfillmentNote}`,
+ ].join("\n")
+ );
+ }
+
+ return {
+ ok: true,
+ status: 200,
+ eventType,
+ paymentId,
+ fulfillmentNote,
+ };
+}
diff --git a/tests/checkout-functions.test.mjs b/tests/checkout-functions.test.mjs
new file mode 100644
index 0000000..5862310
--- /dev/null
+++ b/tests/checkout-functions.test.mjs
@@ -0,0 +1,319 @@
+import assert from "node:assert/strict";
+import { describe, it } from "node:test";
+import { buildAuditCheckoutUrl, buildMcpCheckoutUrl, buildPartnerCheckoutUrl, buildCloudOrPrivateCheckoutUrl } from "../src/lib/checkout-urls.js";
+import { verifyDodoWebhookSignature, handleDodoWebhookEvent } from "../src/lib/dodo-webhook.js";
+import { onRequestPost as mcpOrderPost } from "../functions/api/mcp-order.js";
+import { onRequestPost as auditOrderPost } from "../functions/api/audit-order.js";
+import { onRequestPost as enterprisePost } from "../functions/api/enterprise.js";
+import { onRequestPost as partnerOrderPost } from "../functions/api/partner-order.js";
+import { onRequest as middlewareRequest } from "../functions/_middleware.js";
+import affiliatesData from "../src/data/affiliates.json" with { type: "json" };
+
+function makeContext(body, env = {}) {
+ return {
+ request: new Request("https://ritualdev.in/api/test", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ }),
+ env,
+ };
+}
+
+describe("Storefront Checkout URL Builders & Cloudflare Pages Functions", () => {
+ it("builds consistent MCP Dodo checkout URLs with order metadata", () => {
+ const url349 = buildMcpCheckoutUrl({
+ orderId: "MCP-SVC-101",
+ tier: "standard_349",
+ companyName: "BlackFriday.dev",
+ apiDocsUrl: "https://www.blackfriday.dev",
+ contactEmail: "Zoltan@Bergside.com",
+ });
+ assert.ok(url349.includes("paymentAmount=349"));
+ assert.ok(url349.includes("email=zoltan%40bergside.com"));
+ assert.ok(url349.includes("metadata_orderId=MCP-SVC-101"));
+ assert.ok(url349.includes("mcp-builder%2F%3Fsuccess%3Dtrue"));
+
+ const url699 = buildMcpCheckoutUrl({
+ orderId: "MCP-SVC-102",
+ tier: "pro_699",
+ contactEmail: "cto@acme.io",
+ });
+ assert.ok(url699.includes("paymentAmount=699"));
+ });
+
+ it("maps Starter $199 and Pro $399 audits to distinct Dodo product IDs", () => {
+ const starter = buildAuditCheckoutUrl({
+ orderId: "AUDIT-199",
+ packageTier: "starter_199",
+ targetUrl: "https://thrum.team",
+ contactEmail: "leon@letto.ca",
+ });
+ const pro = buildAuditCheckoutUrl({
+ orderId: "AUDIT-399",
+ packageTier: "pro_399",
+ repoUrl: "https://github.com/traccia-ai/traccia-py",
+ contactEmail: "founder@traccia.ai",
+ });
+ assert.ok(starter.includes("pdt_0NrFWnRHHEI72iaB8AC9r"));
+ assert.ok(pro.includes("pdt_0NrFWnSQHfJhuHs4Y9Xmh"));
+ });
+
+ it("mcp-order Cloudflare function rejects missing email and returns checkoutUrl on valid input", async () => {
+ const bad = await mcpOrderPost(makeContext({ apiDocsUrl: "https://example.com" }));
+ assert.equal(bad.status, 400);
+
+ const ok = await mcpOrderPost(
+ makeContext({
+ companyName: "Acme API",
+ apiDocsUrl: "https://api.acme.com/openapi.json",
+ contactEmail: "founder@acme.com",
+ tier: "standard_349",
+ })
+ );
+ assert.equal(ok.status, 200);
+ const json = await ok.json();
+ assert.equal(json.success, true);
+ assert.ok(json.checkoutUrl.includes("paymentAmount=349"));
+ });
+
+ it("audit-order and enterprise Cloudflare functions validate and return success", async () => {
+ const auditRes = await auditOrderPost(
+ makeContext({
+ productName: "Thrum",
+ targetUrl: "https://thrum.team",
+ contactEmail: "leon@letto.ca",
+ packageTier: "starter_199",
+ })
+ );
+ assert.equal(auditRes.status, 200);
+ const auditJson = await auditRes.json();
+ assert.ok(auditJson.checkoutUrl.includes("pdt_0NrFWnRHHEI72iaB8AC9r"));
+
+ const entRes = await enterprisePost(
+ makeContext({
+ fullName: "Jane Doe",
+ workEmail: "jane@corp.io",
+ companyName: "Corp",
+ })
+ );
+ assert.equal(entRes.status, 200);
+ const entJson = await entRes.json();
+ assert.equal(entJson.success, true);
+ assert.ok(entJson.submissionId.startsWith("SLA-REQ-"));
+ });
+
+ it("partner-order Cloudflare function supports $9/$19/$49 indie tiers and affiliates include cloud bounties", async () => {
+ const pUrl = buildPartnerCheckoutUrl({
+ orderId: "SPONSOR-999",
+ tier: "spotlight_19",
+ toolName: "BlackFriday.dev",
+ toolUrl: "https://www.blackfriday.dev",
+ contactEmail: "zoltan@bergside.com",
+ });
+ assert.ok(pUrl.includes("paymentAmount=19"));
+ assert.ok(pUrl.includes("metadata_toolName=BlackFriday.dev"));
+
+ const partnerRes = await partnerOrderPost(
+ makeContext({
+ toolName: "BlackFriday.dev",
+ toolUrl: "https://www.blackfriday.dev",
+ contactEmail: "zoltan@bergside.com",
+ tier: "starter_9",
+ })
+ );
+ assert.equal(partnerRes.status, 200);
+ const partnerJson = await partnerRes.json();
+ assert.ok(partnerJson.checkoutUrl.includes("paymentAmount=9"));
+
+ const comboRes = await partnerOrderPost(
+ makeContext({
+ toolName: "Akii SEO",
+ toolUrl: "https://github.com/akii-technologies-ltd/akii-seo-ai-search-optimizer",
+ contactEmail: "team@akii.dev",
+ tier: "combo_79",
+ bundleCategories: ["ai", "cli", "cloud"],
+ })
+ );
+ assert.equal(comboRes.status, 200);
+ const comboJson = await comboRes.json();
+ assert.ok(comboJson.checkoutUrl.includes("paymentAmount=79"));
+ assert.ok(comboJson.checkoutUrl.includes("metadata_bundleCategories=ai%2Ccli%2Ccloud"));
+
+ const customRes = await partnerOrderPost(
+ makeContext({
+ sponsorHandle: "@devpatron",
+ contactEmail: "patron@acme.dev",
+ tier: "custom",
+ customAmountUsd: 125,
+ })
+ );
+ assert.equal(customRes.status, 200);
+ const customJson = await customRes.json();
+ assert.ok(customJson.checkoutUrl.includes("paymentAmount=125"));
+ assert.ok(customJson.checkoutUrl.includes("metadata_sponsorHandle=devpatron"));
+
+ const clampedUrl = buildPartnerCheckoutUrl({
+ orderId: "SPONSOR-MIN",
+ tier: "custom",
+ customAmountUsd: 1,
+ sponsorHandle: "smallpatron",
+ contactEmail: "p@acme.dev",
+ });
+ assert.ok(clampedUrl.includes("paymentAmount=5"));
+
+ const slugs = affiliatesData.partners.map((p) => p.slug);
+ for (const expected of ["digitalocean", "railway", "hetzner", "cloudflare", "posthog", "neon", "vultr", "supabase"]) {
+ assert.ok(slugs.includes(expected), `Expected affiliate slug: ${expected}`);
+ }
+ });
+
+ it("verifies Standard Webhooks HMAC signature and rejects invalid or stale Dodo webhooks", async () => {
+ const cloudUrl = buildCloudOrPrivateCheckoutUrl({
+ tier: "private_compliance_kit",
+ customerEmail: "ciso@acme.com",
+ githubUsername: "@octocat",
+ });
+ assert.ok(cloudUrl.includes("pdt_0NrFWnRHHEI72iaB8AC9r"));
+ assert.ok(cloudUrl.includes("metadata_github_username=octocat"));
+
+ const rawSecret = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+ const secretB64 = `whsec_${Buffer.from(rawSecret).toString("base64")}`;
+ const webhookId = "msg_test_123";
+ const ts = Math.floor(Date.now() / 1000).toString();
+ const rawBody = JSON.stringify({
+ type: "payment.succeeded",
+ data: {
+ payment_id: "pay_001",
+ total_amount: 19900,
+ currency: "USD",
+ customer: { email: "buyer@acme.com" },
+ metadata: { tier: "private_compliance_kit", github_username: "octocat" },
+ },
+ });
+
+ const crypto = await import("node:crypto");
+ const sig = crypto
+ .createHmac("sha256", rawSecret)
+ .update(`${webhookId}.${ts}.${rawBody}`)
+ .digest("base64");
+
+ const valid = await verifyDodoWebhookSignature({
+ rawBody,
+ webhookId,
+ webhookTimestamp: ts,
+ webhookSignature: `v1,${sig}`,
+ secret: secretB64,
+ });
+ assert.equal(valid, true);
+
+ const invalid = await verifyDodoWebhookSignature({
+ rawBody: rawBody + " ",
+ webhookId,
+ webhookTimestamp: ts,
+ webhookSignature: `v1,${sig}`,
+ secret: secretB64,
+ });
+ assert.equal(invalid, false);
+
+ const resOk = await handleDodoWebhookEvent({
+ rawBody,
+ headers: new Headers({
+ "webhook-id": webhookId,
+ "webhook-timestamp": ts,
+ "webhook-signature": `v1,${sig}`,
+ }),
+ env: { DODO_WEBHOOK_SECRET: secretB64 },
+ });
+ assert.equal(resOk.ok, true);
+ assert.equal(resOk.status, 200);
+ assert.equal(resOk.paymentId, "pay_001");
+
+ const resBad = await handleDodoWebhookEvent({
+ rawBody,
+ headers: new Headers({
+ "webhook-id": webhookId,
+ "webhook-timestamp": ts,
+ "webhook-signature": "v1,invalidsig",
+ }),
+ env: { DODO_WEBHOOK_SECRET: secretB64 },
+ });
+ assert.equal(resBad.ok, false);
+ assert.equal(resBad.status, 401);
+
+ // Verified sponsor payment dispatches DevShelf activate-sponsor workflow automatically
+ const sponsorBody = JSON.stringify({
+ type: "payment.succeeded",
+ data: {
+ payment_id: "pay_sponsor_79",
+ total_amount: 7900,
+ currency: "USD",
+ customer: { email: "founder@akii.dev" },
+ metadata: {
+ orderId: "SPONSOR-777",
+ tier: "combo_79",
+ toolName: "Akii SEO",
+ toolUrl: "https://github.com/akii-technologies-ltd/akii-seo-ai-search-optimizer",
+ category: "cli",
+ },
+ },
+ });
+ const sponsorSig = crypto
+ .createHmac("sha256", rawSecret)
+ .update(`${webhookId}.${ts}.${sponsorBody}`)
+ .digest("base64");
+
+ const origFetch = globalThis.fetch;
+ let dispatchedUrl = "";
+ let dispatchedTier = "";
+ globalThis.fetch = async (url, init) => {
+ dispatchedUrl = String(url);
+ const bodyObj = JSON.parse(init?.body || "{}");
+ dispatchedTier = bodyObj?.inputs?.tier || "";
+ return new Response(null, { status: 204 });
+ };
+ try {
+ const sponsorHookRes = await handleDodoWebhookEvent({
+ rawBody: sponsorBody,
+ headers: new Headers({
+ "webhook-id": webhookId,
+ "webhook-timestamp": ts,
+ "webhook-signature": `v1,${sponsorSig}`,
+ }),
+ env: { DODO_WEBHOOK_SECRET: secretB64, GITHUB_PAT: "ghp_testtoken" },
+ });
+ assert.equal(sponsorHookRes.ok, true);
+ assert.ok(dispatchedUrl.includes("activate-sponsor.yml/dispatches"));
+ assert.equal(dispatchedTier, "enterprise");
+ assert.ok(sponsorHookRes.fulfillmentNote.includes("activate-sponsor.yml"));
+ } finally {
+ globalThis.fetch = origFetch;
+ }
+ });
+
+ it("redirects donation.ritualdev.in and www.ritualdev.in to canonical ritualdev.in routes", async () => {
+ const donRes = await middlewareRequest({
+ request: new Request("https://donation.ritualdev.in/?src=qr"),
+ next: () => new Response("ok"),
+ });
+ assert.equal(donRes.status, 302);
+ assert.equal(donRes.headers.get("location"), "https://ritualdev.in/donate/?src=qr");
+
+ const wwwRes = await middlewareRequest({
+ request: new Request("https://www.ritualdev.in/partners?x=1"),
+ next: () => new Response("ok"),
+ });
+ assert.equal(wwwRes.status, 301);
+ assert.equal(wwwRes.headers.get("location"), "https://ritualdev.in/partners?x=1");
+
+ let calledNext = false;
+ await middlewareRequest({
+ request: new Request("https://ritualdev.in/donate/"),
+ next: () => {
+ calledNext = true;
+ return new Response("ok");
+ },
+ });
+ assert.equal(calledNext, true);
+ });
+});