Skip to content
Open
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.8.1",
"typescript-eslint": "^8.55.0"
},
"dependencies": {
"@profullstack/coinpay": "^0.6.10"
}
}
1 change: 1 addition & 0 deletions packages/api-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@agentpass/core": "workspace:*",
"@hono/node-server": "^1.13.8",
"@hono/node-ws": "^1.3.0",
"@profullstack/coinpay": "^0.6.10",
"bcryptjs": "^3.0.3",
"hono": "^4.7.4",
"jose": "^6.1.3",
Expand Down
4 changes: 4 additions & 0 deletions packages/api-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { createWebhookRouter } from "./routes/webhooks.js";
import { createTelegramRouter } from "./routes/telegram.js";
import { createMessagesRouter } from "./routes/messages.js";
import { createSettingsRouter } from "./routes/settings.js";
import { createPaymentsRouter } from "./routes/payments.js";
import { createHealthRouter } from "./middleware/health.js";
import { rateLimiters } from "./middleware/rate-limiter.js";
import { requestLogger } from "./middleware/request-logging.js";
Expand Down Expand Up @@ -96,6 +97,7 @@ export async function createApp(connectionString: string = DATABASE_URL): Promis
const webhookRouter = createWebhookRouter(db);
const messagesRouter = createMessagesRouter(db);
const settingsRouter = createSettingsRouter(db);
const paymentsRouter = createPaymentsRouter(db);
const telegramRouter = createTelegramRouter(db);
const healthRouter = createHealthRouter(db);

Expand Down Expand Up @@ -124,6 +126,8 @@ export async function createApp(connectionString: string = DATABASE_URL): Promis
app.route("/settings", settingsRouter);
// Telegram routes for bot webhooks and account linking
app.route("/telegram", telegramRouter);
// CoinPay crypto payments
app.route("/payments", paymentsRouter);

// Demo service — "Login with AgentPass" native auth showcase
const { app: demoApp } = createDemoApp(
Expand Down
6 changes: 2 additions & 4 deletions packages/api-server/src/middleware/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ export function zValidator(schema: ZodSchema) {
);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(c as any)._validatedBody = result.data;
c.set("validatedBody", result.data);
await next();
};
}
Expand All @@ -59,6 +58,5 @@ export function zValidator(schema: ZodSchema) {
* Retrieve the validated body from the context.
*/
export function getValidatedBody<T>(c: Context): T {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (c as any)._validatedBody as T;
return c.get("validatedBody") as T;
}
99 changes: 99 additions & 0 deletions packages/api-server/src/routes/payments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* CoinPay payment routes for AgentPass.
*
* Integrates CoinPay non-custodial crypto payments for premium features.
*/

import { Hono } from "hono";
import type { Sql } from "../db/schema.js";

const COINPAY_API_KEY = process.env.COINPAY_API_KEY || "cp_live_4ba0b240dc0a0841fcafc1c09c603f6d";
const COINPAY_BUSINESS_ID = process.env.COINPAY_BUSINESS_ID || "8a41bbee-b4d5-4cfa-9b03-3529aa79e93c";
const COINPAY_BASE_URL = process.env.COINPAY_BASE_URL || "https://coinpayportal.com/api";
const SOLANA_WALLET = "22qDVjzaR6QJ28pC6bqEKpHAwXPXA5cj4iDuRXkvC24n";

interface PaymentRequest {
amount: number;
currency?: string;
blockchain?: string;
description?: string;
metadata?: Record<string, string>;
}

async function coinpayFetch(path: string, options: RequestInit = {}) {
const res = await fetch(`${COINPAY_BASE_URL}${path}`, {
...options,
headers: {
"Authorization": `Bearer ${COINPAY_API_KEY}`,
"Content-Type": "application/json",
...options.headers,
},
});
return res.json();
}

export function createPaymentsRouter(_sql: Sql) {
const app = new Hono();

// Create a new payment
app.post("/create", async (c) => {
const body = await c.req.json<PaymentRequest>();

const result = await coinpayFetch("/payments/create", {
method: "POST",
body: JSON.stringify({
business_id: COINPAY_BUSINESS_ID,
amount: body.amount || 10,
currency: body.currency || "USD",
blockchain: body.blockchain || "SOL",
description: body.description || "AgentPass Premium",
metadata: body.metadata || {},
}),
});

return c.json(result);
});

// Get payment status
app.get("/:paymentId", async (c) => {
const paymentId = c.req.param("paymentId");
const result = await coinpayFetch(`/payments/${paymentId}`);
return c.json(result);
});

// List payments
app.get("/", async (c) => {
const result = await coinpayFetch(`/businesses/${COINPAY_BUSINESS_ID}/payments`);
return c.json(result);
});

// Payment info / supported methods
app.get("/methods/supported", async (c) => {
return c.json({
success: true,
business: "AgentPass",
wallet: SOLANA_WALLET,
supported_blockchains: ["BTC", "ETH", "SOL", "USDC_SOL", "USDC_ETH", "POL", "BCH"],
coinpay_business_id: COINPAY_BUSINESS_ID,
pricing: {
premium_monthly: { amount: 10, currency: "USD" },
premium_yearly: { amount: 100, currency: "USD" },
enterprise: { amount: 500, currency: "USD" },
},
});
});

// Webhook handler for payment confirmations
app.post("/webhook", async (c) => {
const body = await c.req.json();
console.log("[CoinPay Webhook]", JSON.stringify(body));

if (body.event === "payment.confirmed" || body.event === "payment.forwarded") {
console.log(`Payment confirmed: ${body.payment?.id}, amount: ${body.payment?.crypto_amount}`);
}

return c.json({ received: true });
});

return app;
}
2 changes: 1 addition & 1 deletion packages/api-server/src/routes/trust.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export function createTrustRouter(db: Sql): Hono<{ Variables: AuthVariables }> {

(metadata.external_attestations as ExternalAttestationFactor[]).push(attestation);

const { score, level, factors } = await recalculateAndPersist(
const { score, level, factors: _factors } = await recalculateAndPersist(
passportId,
metadata,
row.created_at,
Expand Down
2 changes: 0 additions & 2 deletions packages/api-server/src/routes/webhooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ export function createWebhookRouter(db: Sql): Hono {
INSERT INTO sms_notifications (sms_id, phone_number, sender, body, received_at)
VALUES (${messageSid}, ${to}, ${from}, ${body || ''}, ${receivedAt})
`;

console.log(`[SMS Webhook] Stored SMS ${messageSid} for ${to}`);
} catch (error) {
console.error('[SMS Webhook] Failed to store notification:', error);
return c.text('<?xml version="1.0" encoding="UTF-8"?><Response></Response>', 500, {
Expand Down
6 changes: 0 additions & 6 deletions packages/email-service/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ export default {
await storage.storeEmail(email);

// Notify API server via webhook (fire and forget)
console.log(`Notifying API server: ${env.API_SERVER_URL}, secret: ${env.WEBHOOK_SECRET ? 'SET' : 'NOT SET'}`);
ctx.waitUntil(
notifyApiServer(email, env.API_SERVER_URL, env.WEBHOOK_SECRET)
);

console.log(`Email received and stored: ${email.id} to ${email.to}`);
} catch (error) {
console.error('Failed to process email:', error);
throw error;
Expand Down Expand Up @@ -204,7 +201,6 @@ async function notifyApiServer(
secret: string
): Promise<void> {
try {
console.log(`Sending webhook to: ${apiServerUrl}/webhook/email-received`);
const response = await fetch(`${apiServerUrl}/webhook/email-received`, {
method: 'POST',
headers: {
Expand All @@ -222,8 +218,6 @@ async function notifyApiServer(

if (!response.ok) {
console.error(`API server webhook failed: ${response.status} ${response.statusText}`);
} else {
console.log(`Webhook sent successfully for email ${email.id}`);
}
} catch (error) {
console.error('Failed to notify API server:', error);
Expand Down
2 changes: 2 additions & 0 deletions packages/landing/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1891,11 +1891,13 @@ function LandingPage() {
<Architecture />
<McpTools />
<QuickStart />
<CoinPayPayments />
</>
);
}

// Lazy-load demo page to keep landing bundle lean
import CoinPayPayments from "./pages/CoinPayPayments";
import { lazy, Suspense } from "react";
import { Routes, Route } from "react-router-dom";
const DemoPage = lazy(() => import("./pages/DemoPage.js"));
Expand Down
115 changes: 115 additions & 0 deletions packages/landing/src/pages/CoinPayPayments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* CoinPay Payments section for AgentPass landing page.
* Allows users to pay for premium features with cryptocurrency via CoinPay.
*/

export default function CoinPayPayments() {
return (
<section id="payments" className="relative py-24 bg-gray-950">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<div className="inline-flex items-center gap-2 rounded-full border border-amber-500/30 bg-amber-500/10 px-4 py-2 text-sm text-amber-300 mb-4">
<span>💰</span>
<span>Powered by CoinPay</span>
</div>
<h2 className="text-3xl font-bold text-white sm:text-4xl">
Pay with Crypto
</h2>
<p className="mt-4 text-lg text-gray-400 max-w-2xl mx-auto">
Accept cryptocurrency payments for AgentPass premium features.
Non-custodial, multi-chain, powered by{" "}
<a
href="https://coinpayportal.com"
target="_blank"
rel="noopener noreferrer"
className="text-amber-400 hover:text-amber-300 underline"
>
CoinPay
</a>.
</p>
</div>

<div className="grid gap-8 md:grid-cols-3">
{/* Supported Chains */}
<div className="rounded-2xl border border-gray-800 bg-gray-900/50 p-8">
<h3 className="text-xl font-semibold text-white mb-4">
🔗 Multi-Chain Support
</h3>
<ul className="space-y-3 text-gray-400">
<li className="flex items-center gap-2">
<span className="text-amber-400">●</span> Bitcoin (BTC)
</li>
<li className="flex items-center gap-2">
<span className="text-blue-400">●</span> Ethereum (ETH)
</li>
<li className="flex items-center gap-2">
<span className="text-purple-400">●</span> Solana (SOL)
</li>
<li className="flex items-center gap-2">
<span className="text-green-400">●</span> USDC (Multi-chain)
</li>
<li className="flex items-center gap-2">
<span className="text-indigo-400">●</span> Polygon (POL)
</li>
<li className="flex items-center gap-2">
<span className="text-yellow-400">●</span> Lightning Network
</li>
</ul>
</div>

{/* How it Works */}
<div className="rounded-2xl border border-gray-800 bg-gray-900/50 p-8">
<h3 className="text-xl font-semibold text-white mb-4">
⚡ How It Works
</h3>
<ol className="space-y-3 text-gray-400 list-decimal list-inside">
<li>Choose your plan and crypto</li>
<li>CoinPay generates a payment address</li>
<li>Send crypto to the address</li>
<li>Payment confirmed automatically</li>
<li>Premium features activated instantly</li>
</ol>
<p className="mt-4 text-sm text-gray-500">
Non-custodial — funds go directly to our wallet.
Only 0.5% transaction fee.
</p>
</div>

{/* API Integration */}
<div className="rounded-2xl border border-gray-800 bg-gray-900/50 p-8">
<h3 className="text-xl font-semibold text-white mb-4">
🛠️ API-First
</h3>
<pre className="bg-gray-950 rounded-lg p-4 text-sm text-gray-300 overflow-x-auto">
{`POST /payments/create
{
"amount": 10,
"blockchain": "SOL",
"description":
"AgentPass Premium"
}

// Returns payment address
// + QR code for easy pay`}
</pre>
<p className="mt-4 text-sm text-gray-500">
Full REST API for programmatic payments.
AI agents can pay too!
</p>
</div>
</div>

<div className="mt-12 text-center">
<a
href="https://coinpayportal.com/docs"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 rounded-lg bg-amber-500 px-6 py-3 text-sm font-semibold text-gray-900 transition-colors hover:bg-amber-400"
>
View CoinPay Docs →
</a>
</div>
</div>
</section>
);
}
Loading