diff --git a/package.json b/package.json index 877af9c..341968b 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/packages/api-server/package.json b/packages/api-server/package.json index 21ead88..c87e3f3 100644 --- a/packages/api-server/package.json +++ b/packages/api-server/package.json @@ -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", diff --git a/packages/api-server/src/index.ts b/packages/api-server/src/index.ts index 6c5b3db..04e5a62 100644 --- a/packages/api-server/src/index.ts +++ b/packages/api-server/src/index.ts @@ -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"; @@ -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); @@ -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( diff --git a/packages/api-server/src/middleware/validation.ts b/packages/api-server/src/middleware/validation.ts index 5e76788..5a31dc7 100644 --- a/packages/api-server/src/middleware/validation.ts +++ b/packages/api-server/src/middleware/validation.ts @@ -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(); }; } @@ -59,6 +58,5 @@ export function zValidator(schema: ZodSchema) { * Retrieve the validated body from the context. */ export function getValidatedBody(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; } diff --git a/packages/api-server/src/routes/payments.ts b/packages/api-server/src/routes/payments.ts new file mode 100644 index 0000000..abd155a --- /dev/null +++ b/packages/api-server/src/routes/payments.ts @@ -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; +} + +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(); + + 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; +} diff --git a/packages/api-server/src/routes/trust.ts b/packages/api-server/src/routes/trust.ts index aa46438..aa8f308 100644 --- a/packages/api-server/src/routes/trust.ts +++ b/packages/api-server/src/routes/trust.ts @@ -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, diff --git a/packages/api-server/src/routes/webhooks.ts b/packages/api-server/src/routes/webhooks.ts index 4c986da..34d4336 100644 --- a/packages/api-server/src/routes/webhooks.ts +++ b/packages/api-server/src/routes/webhooks.ts @@ -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('', 500, { diff --git a/packages/email-service/src/worker.ts b/packages/email-service/src/worker.ts index eb5694d..190769a 100644 --- a/packages/email-service/src/worker.ts +++ b/packages/email-service/src/worker.ts @@ -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; @@ -204,7 +201,6 @@ async function notifyApiServer( secret: string ): Promise { try { - console.log(`Sending webhook to: ${apiServerUrl}/webhook/email-received`); const response = await fetch(`${apiServerUrl}/webhook/email-received`, { method: 'POST', headers: { @@ -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); diff --git a/packages/landing/src/App.tsx b/packages/landing/src/App.tsx index 2bb93c6..aaca08a 100644 --- a/packages/landing/src/App.tsx +++ b/packages/landing/src/App.tsx @@ -1891,11 +1891,13 @@ function LandingPage() { + ); } // 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")); diff --git a/packages/landing/src/pages/CoinPayPayments.tsx b/packages/landing/src/pages/CoinPayPayments.tsx new file mode 100644 index 0000000..2d9eb58 --- /dev/null +++ b/packages/landing/src/pages/CoinPayPayments.tsx @@ -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 ( +
+
+
+
+ 💰 + Powered by CoinPay +
+

+ Pay with Crypto +

+

+ Accept cryptocurrency payments for AgentPass premium features. + Non-custodial, multi-chain, powered by{" "} + + CoinPay + . +

+
+ +
+ {/* Supported Chains */} +
+

+ 🔗 Multi-Chain Support +

+
    +
  • + Bitcoin (BTC) +
  • +
  • + Ethereum (ETH) +
  • +
  • + Solana (SOL) +
  • +
  • + USDC (Multi-chain) +
  • +
  • + Polygon (POL) +
  • +
  • + Lightning Network +
  • +
+
+ + {/* How it Works */} +
+

+ ⚡ How It Works +

+
    +
  1. Choose your plan and crypto
  2. +
  3. CoinPay generates a payment address
  4. +
  5. Send crypto to the address
  6. +
  7. Payment confirmed automatically
  8. +
  9. Premium features activated instantly
  10. +
+

+ Non-custodial — funds go directly to our wallet. + Only 0.5% transaction fee. +

+
+ + {/* API Integration */} +
+

+ 🛠️ API-First +

+
+{`POST /payments/create
+{
+  "amount": 10,
+  "blockchain": "SOL",
+  "description":
+    "AgentPass Premium"
+}
+
+// Returns payment address
+// + QR code for easy pay`}
+            
+

+ Full REST API for programmatic payments. + AI agents can pay too! +

+
+
+ + +
+
+ ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b21454c..38ba71f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + '@profullstack/coinpay': + specifier: ^0.6.10 + version: 0.6.10 devDependencies: '@eslint/js': specifier: ^9.39.2 @@ -35,6 +39,9 @@ importers: '@hono/node-ws': specifier: ^1.3.0 version: 1.3.0(@hono/node-server@1.19.9(hono@4.11.9))(hono@4.11.9) + '@profullstack/coinpay': + specifier: ^0.6.10 + version: 0.6.10 bcryptjs: specifier: ^3.0.3 version: 3.0.3 @@ -765,6 +772,19 @@ packages: '@cfworker/json-schema': optional: true + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@profullstack/coinpay@0.6.10': + resolution: {integrity: sha512-DlS6FW1pt/oqXbHMuYQB/aX891aH3kOy3k+t9y+uqDwvDPeCxnJ9ZfMyOEEqi0ZTxtXc8jZxywnSv7/SoyVJkA==} + engines: {node: '>=20.0.0'} + hasBin: true + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -802,79 +822,66 @@ packages: resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.57.1': resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.57.1': resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.57.1': resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.57.1': resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.57.1': resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.57.1': resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.57.1': resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.57.1': resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.57.1': resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.57.1': resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.57.1': resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.57.1': resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.57.1': resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} @@ -906,6 +913,15 @@ packages: cpu: [x64] os: [win32] + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@tailwindcss/node@4.1.18': resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} @@ -944,28 +960,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.18': resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.18': resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.18': resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.18': resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} @@ -1810,28 +1822,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} @@ -2056,6 +2064,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prelude-ls@1.2.1: @@ -2868,6 +2877,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.8.0': {} + + '@profullstack/coinpay@0.6.10': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rollup/rollup-android-arm-eabi@4.57.1': @@ -2945,6 +2967,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.57.1': optional: true + '@scure/base@1.2.6': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@tailwindcss/node@4.1.18': dependencies: '@jridgewell/remapping': 2.3.5