flow-nexus
Version:
🚀 AI-Powered Swarm Intelligence Platform - Gamified MCP Development with 70+ Tools
735 lines (663 loc) • 22.3 kB
JavaScript
/**
* Flow-Nexus MCP Payment Tools - SECURED VERSION
* Enhanced with rate limiting, input validation, and secure vault access
* PCI DSS SAQ-A compliant - No card data stored or processed
*/
import chalk from 'chalk';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
import { rateLimiter } from '../middleware/rate-limiter.js';
import { inputValidator } from '../middleware/input-validator.js';
import { vaultSecurity } from '../middleware/vault-security.js';
// Load environment variables from root .env (suppress dotenv output)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Temporarily suppress console.log to hide dotenv message
const originalLog = console.log;
console.log = () => {};
dotenv.config({ path: path.resolve(__dirname, '../../../../.env') });
console.log = originalLog;
// Security constants
const MIN_AMOUNT = 10; // Minimum $10 to prevent micro-transaction abuse
const MAX_AMOUNT = 10000; // Maximum $10,000 for fraud prevention
const CREDIT_RATE = 10; // 1 USD = 10 credits
const BONUS_THRESHOLD = 100; // $100+ gets bonus
const BONUS_RATE = 1.1; // 10% bonus
/**
* Enhanced authentication validation with session expiry
*/
function validateAuth(context, requiredRole = null) {
// Check if user is authenticated
if (!context?.user?.id || !context?.session) {
return {
valid: false,
error: 'Authentication required. Please log in.',
};
}
// Check session expiry
if (context.session?.expires_at) {
const sessionExpiry = new Date(context.session.expires_at);
if (sessionExpiry < new Date()) {
return {
valid: false,
error: 'Session expired. Please log in again.',
};
}
}
// Check email confirmation
if (!context.user?.email_confirmed_at) {
return {
valid: false,
error: 'Please confirm your email address.',
};
}
// Check if user is banned
if (context.user?.banned === true) {
return {
valid: false,
error: 'Account suspended.',
};
}
// Check admin role with additional verification
if (requiredRole === 'admin') {
const isAdmin =
context.user?.app_metadata?.role === 'admin' &&
context.user?.app_metadata?.admin_verified === true;
const isSuperAdmin = context.user?.email === 'ruv@ruv.net';
if (!isAdmin && !isSuperAdmin) {
return {
valid: false,
error: 'Admin permission required for this operation.',
};
}
}
return {
valid: true,
userId: context.user.id,
email: context.user.email,
sessionId: context.session?.id,
};
}
/**
* Calculate credits with bonus logic
*/
function calculateCredits(amount) {
let credits = amount * CREDIT_RATE;
if (amount >= BONUS_THRESHOLD) {
credits *= BONUS_RATE;
}
return Math.round(credits * 100) / 100;
}
/**
* Enhanced Supabase function call with vault security
*/
async function callSecureSupabaseFunction(supabase, functionName, payload, userId, context) {
try {
// Generate vault token for this operation
const vaultToken = await vaultSecurity.generateVaultToken(
userId,
'payment_vault_access',
context
);
// Call edge function with enhanced security
const { data, error } = await supabase.functions.invoke(functionName, {
body: {
...payload,
user_id: userId,
vault_token: vaultToken,
},
headers: {
Authorization: `Bearer ${context.session?.access_token}`,
'X-Request-ID': crypto.randomUUID(),
'X-User-Fingerprint': vaultSecurity.generateFingerprint(context),
},
});
if (error) throw error;
return data;
} catch (error) {
console.error(chalk.red(`Secure function error: ${error.message}`));
throw error;
}
}
/**
* Register secured payment tools with the MCP server
*/
export function registerSecuredPaymentTools(server, supabase) {
// Tool: check_balance (with rate limiting)
server.addTool({
name: 'check_balance',
description: 'Check current credit balance and auto-refill status',
inputSchema: {
type: 'object',
properties: {},
},
handler: async (params, context) => {
try {
// Rate limiting
const rateLimit = rateLimiter.checkLimit('check_balance', context, context.ip);
if (!rateLimit.allowed) {
return {
success: false,
error: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
};
}
// Validate authentication
const auth = validateAuth(context);
if (!auth.valid) {
return { success: false, error: auth.error };
}
// Use Supabase RLS to get user's own profile
const { data: profile, error } = await supabase
.from('profiles')
.select('credits_balance, ruv_credits, auto_refill_enabled, auto_refill_threshold, auto_refill_amount')
.eq('id', auth.userId)
.single();
if (error) {
throw new Error('Failed to fetch balance');
}
const balance = profile.credits_balance || profile.ruv_credits || 0;
const lowBalance = balance < 20;
// Check if auto-refill should trigger
if (lowBalance && profile.auto_refill_enabled) {
await supabase.rpc('check_auto_refill', {
p_user_id: auth.userId,
});
}
return {
success: true,
balance: balance,
auto_refill_enabled: profile.auto_refill_enabled,
auto_refill_threshold: profile.auto_refill_threshold,
auto_refill_amount: profile.auto_refill_amount,
low_balance_warning: lowBalance,
message: `Current balance: ${balance} credits${lowBalance ? ' (⚠️ LOW BALANCE)' : ''}`,
rateLimit: rateLimit.headers,
};
} catch (error) {
return {
success: false,
error: 'Failed to check balance. Please try again.',
};
}
},
});
// Tool: create_payment_link (with validation and rate limiting)
server.addTool({
name: 'create_payment_link',
description: 'Create a secure payment link for purchasing credits',
inputSchema: {
type: 'object',
properties: {
amount: {
type: 'number',
description: 'Amount in USD (minimum $10)',
minimum: 10,
maximum: 10000,
},
},
required: ['amount'],
},
handler: async (params, context) => {
try {
// Input validation
const validation = inputValidator.validate('create_payment_link', params);
if (!validation.valid) {
return {
success: false,
error: 'Validation failed',
errors: validation.errors,
};
}
// Rate limiting
const rateLimit = rateLimiter.checkLimit('create_payment_link', context, context.ip);
if (!rateLimit.allowed) {
return {
success: false,
error: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
};
}
// Validate authentication
const auth = validateAuth(context);
if (!auth.valid) {
return { success: false, error: auth.error };
}
const amount = validation.sanitized.amount;
// Additional amount validation
if (amount < MIN_AMOUNT || amount > MAX_AMOUNT) {
return {
success: false,
error: `Amount must be between $${MIN_AMOUNT} and $${MAX_AMOUNT}`,
};
}
// Calculate credits with bonus
const credits = calculateCredits(amount);
// Call secure edge function with vault token
const result = await callSecureSupabaseFunction(
supabase,
'create-payment-link',
{
amount: amount,
credits: credits,
type: 'deposit',
},
auth.userId,
context
);
if (!result.success) {
throw new Error(result.error || 'Failed to create payment link');
}
return {
success: true,
payment_url: result.payment_url,
amount: amount,
credits: credits,
expires_at: result.expires_at,
message: `Payment link created: ${result.payment_url}`,
rateLimit: rateLimit.headers,
};
} catch (error) {
return {
success: false,
error: 'Failed to create payment link. Please try again.',
};
}
},
});
// Tool: configure_auto_refill (with validation)
server.addTool({
name: 'configure_auto_refill',
description: 'Configure automatic credit refill settings',
inputSchema: {
type: 'object',
properties: {
enabled: {
type: 'boolean',
description: 'Enable or disable auto-refill',
},
threshold: {
type: 'number',
description: 'Credit threshold to trigger refill',
minimum: 10,
},
amount: {
type: 'number',
description: 'Amount in USD to refill',
minimum: 10,
},
},
required: ['enabled'],
},
handler: async (params, context) => {
try {
// Input validation
const validation = inputValidator.validate('configure_auto_refill', params);
if (!validation.valid) {
return {
success: false,
error: 'Validation failed',
errors: validation.errors,
};
}
// Rate limiting
const rateLimit = rateLimiter.checkLimit('configure_auto_refill', context, context.ip);
if (!rateLimit.allowed) {
return {
success: false,
error: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
};
}
// Validate authentication
const auth = validateAuth(context);
if (!auth.valid) {
return { success: false, error: auth.error };
}
// Check if user has payment method
const { data: profile } = await supabase
.from('profiles')
.select('stripe_payment_method_id')
.eq('id', auth.userId)
.single();
if (validation.sanitized.enabled && !profile?.stripe_payment_method_id) {
return {
success: false,
error: 'No payment method on file. Complete a payment first.',
};
}
// Update settings using Supabase with RLS
const updates = {
auto_refill_enabled: validation.sanitized.enabled,
updated_at: new Date().toISOString(),
};
if (validation.sanitized.threshold !== undefined) {
updates.auto_refill_threshold = validation.sanitized.threshold;
}
if (validation.sanitized.amount !== undefined) {
updates.auto_refill_amount = validation.sanitized.amount;
}
const { error } = await supabase
.from('profiles')
.update(updates)
.eq('id', auth.userId);
if (error) {
throw new Error('Failed to update settings');
}
// Log configuration change
await supabase
.from('audit_logs')
.insert({
user_id: auth.userId,
action: 'auto_refill_config',
details: {
enabled: validation.sanitized.enabled,
threshold: validation.sanitized.threshold,
amount: validation.sanitized.amount,
},
});
return {
success: true,
enabled: validation.sanitized.enabled,
threshold: validation.sanitized.threshold,
amount: validation.sanitized.amount,
message: `Auto-refill ${validation.sanitized.enabled ? 'enabled' : 'disabled'}`,
rateLimit: rateLimit.headers,
};
} catch (error) {
return {
success: false,
error: 'Failed to configure auto-refill. Please try again.',
};
}
},
});
// Tool: get_payment_history (with validation and rate limiting)
server.addTool({
name: 'get_payment_history',
description: 'Get recent payment and transaction history',
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'number',
description: 'Number of transactions to return',
minimum: 1,
maximum: 100,
default: 10,
},
},
},
handler: async (params, context) => {
try {
// Input validation
const validation = inputValidator.validate('get_payment_history', params);
if (!validation.valid) {
return {
success: false,
error: 'Validation failed',
errors: validation.errors,
};
}
// Rate limiting
const rateLimit = rateLimiter.checkLimit('get_payment_history', context, context.ip);
if (!rateLimit.allowed) {
return {
success: false,
error: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
};
}
// Validate authentication
const auth = validateAuth(context);
if (!auth.valid) {
return { success: false, error: auth.error };
}
const limit = validation.sanitized.limit || 10;
// Validate SQL params to prevent injection
const sqlCheck = inputValidator.validateSQLParams({ limit });
if (!sqlCheck.valid) {
return {
success: false,
error: sqlCheck.error,
};
}
// Use Supabase with RLS to get user's own transactions
const { data: transactions, error } = await supabase
.from('stripe_transactions')
.select('id, type, status, amount, credits_purchased, created_at')
.eq('user_id', auth.userId)
.order('created_at', { ascending: false })
.limit(limit);
if (error) {
throw new Error('Failed to fetch history');
}
// Sanitize transaction data
const sanitized = transactions?.map(t => ({
id: t.id,
type: t.type,
status: t.status,
amount: t.amount,
credits: t.credits_purchased,
date: t.created_at,
})) || [];
return {
success: true,
transactions: sanitized,
count: sanitized.length,
message: `Found ${sanitized.length} transactions`,
rateLimit: rateLimit.headers,
};
} catch (error) {
return {
success: false,
error: 'Failed to fetch payment history.',
};
}
},
});
// Tool: create_subscription (with enhanced validation)
server.addTool({
name: 'create_subscription',
description: 'Create a monthly subscription for automatic credits',
inputSchema: {
type: 'object',
properties: {
plan: {
type: 'string',
enum: ['starter', 'pro', 'enterprise'],
description: 'Subscription plan',
},
},
required: ['plan'],
},
handler: async (params, context) => {
try {
// Input validation
const validation = inputValidator.validate('create_subscription', params);
if (!validation.valid) {
return {
success: false,
error: 'Validation failed',
errors: validation.errors,
};
}
// Rate limiting (strict for subscription creation)
const rateLimit = rateLimiter.checkLimit('create_subscription', context, context.ip);
if (!rateLimit.allowed) {
return {
success: false,
error: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
};
}
// Validate authentication
const auth = validateAuth(context);
if (!auth.valid) {
return { success: false, error: auth.error };
}
// Get user profile with payment info
const { data: profile } = await supabase
.from('profiles')
.select('stripe_customer_id')
.eq('id', auth.userId)
.single();
if (!profile?.stripe_customer_id) {
return {
success: false,
error: 'Complete a payment first to set up subscription',
};
}
// Call secure edge function
const result = await callSecureSupabaseFunction(
supabase,
'create-subscription',
{
plan: validation.sanitized.plan,
customer_id: profile.stripe_customer_id,
},
auth.userId,
context
);
if (!result.success) {
throw new Error(result.error || 'Failed to create subscription');
}
return {
success: true,
subscription_id: result.subscription_id,
plan: validation.sanitized.plan,
price: result.price,
credits_per_month: result.credits,
message: result.message,
rateLimit: rateLimit.headers,
};
} catch (error) {
return {
success: false,
error: 'Failed to create subscription. Please try again.',
};
}
},
});
// Tool: reduce_credits (Admin only with strict validation)
server.addTool({
name: 'reduce_credits',
description: 'Reduce user credits for alpha testing (admin only)',
inputSchema: {
type: 'object',
properties: {
email: {
type: 'string',
description: 'User email address',
},
target_balance: {
type: 'number',
description: 'Target credit balance',
minimum: 0,
},
},
required: ['email', 'target_balance'],
},
handler: async (params, context) => {
try {
// Input validation
const validation = inputValidator.validate('reduce_credits', params);
if (!validation.valid) {
return {
success: false,
error: 'Validation failed',
errors: validation.errors,
};
}
// Rate limiting for admin operations
const rateLimit = rateLimiter.checkLimit('reduce_credits', context, context.ip);
if (!rateLimit.allowed) {
return {
success: false,
error: rateLimit.reason,
retryAfter: rateLimit.retryAfter,
};
}
// Validate admin authentication with enhanced checks
const auth = validateAuth(context, 'admin');
if (!auth.valid) {
return { success: false, error: auth.error };
}
// Generate vault token for admin operation
const vaultToken = await vaultSecurity.generateVaultToken(
auth.userId,
'admin_vault_access',
context
);
// Call Supabase RPC function for admin operations
const { data: result, error } = await supabase.rpc('admin_reduce_credits', {
p_admin_id: auth.userId,
p_target_email: validation.sanitized.email,
p_target_balance: validation.sanitized.target_balance,
p_vault_token: vaultToken,
});
if (error) {
throw new Error('Admin operation failed');
}
// Check if notifications will trigger
const notifications = [];
if (validation.sanitized.target_balance < 20) {
notifications.push('Low balance notification will trigger');
}
if (validation.sanitized.target_balance === 0) {
notifications.push('User will be blocked from paid tools');
}
// Enhanced audit logging
await supabase
.from('audit_logs')
.insert({
user_id: auth.userId,
action: 'admin_credit_reduction',
severity: 'high',
details: {
target_email: validation.sanitized.email,
target_balance: validation.sanitized.target_balance,
reason: 'alpha_testing',
admin_email: auth.email,
session_id: auth.sessionId,
},
ip_address: context.ip,
user_agent: context.userAgent,
});
return {
success: true,
email: validation.sanitized.email,
new_balance: validation.sanitized.target_balance,
notifications: notifications,
message: `Credits reduced to ${validation.sanitized.target_balance} for ${validation.sanitized.email}`,
rateLimit: rateLimit.headers,
};
} catch (error) {
return {
success: false,
error: 'Admin operation failed. Check permissions.',
};
}
},
});
// Log to stderr only to avoid breaking MCP protocol
if (process.stderr && process.env.MCP_MODE !== 'stdio') {
console.error(chalk.green('✅ SECURED Payment tools registered with flow-nexus MCP server'));
console.error(chalk.gray(' - Rate limiting enabled'));
console.error(chalk.gray(' - Input validation active'));
console.error(chalk.gray(' - Vault security enhanced'));
console.error(chalk.gray(' - Session expiry checking'));
console.error(chalk.gray(' - SQL injection prevention'));
console.error(chalk.gray(' - Enhanced audit logging'));
}
}
// Export individual tool names for testing
export const securedPaymentTools = {
check_balance: 'check_balance',
create_payment_link: 'create_payment_link',
configure_auto_refill: 'configure_auto_refill',
get_payment_history: 'get_payment_history',
create_subscription: 'create_subscription',
reduce_credits: 'reduce_credits',
};