@agentdao/core
Version:
Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration
790 lines (789 loc) • 31.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnhancedWeb3SubscriptionSkill = void 0;
const ethers_1 = require("ethers");
const Web3SubscriptionSkill_1 = require("./Web3SubscriptionSkill");
class EnhancedWeb3SubscriptionSkill extends Web3SubscriptionSkill_1.Web3SubscriptionSkill {
/**
* Get default configuration for basic mode
*/
static getDefaultConfig(agentId) {
return {
agentId,
agentName: `${agentId} Agent`,
domain: 'agentdao.com',
mode: 'basic',
adaoToken: {
address: process.env.ADAO_CONTRACT_ADDRESS || "0x1ef7Be0aBff7d1490e952eC1C7476443A66d6b72", // Real ADAO address
decimals: 18,
network: 'base'
},
plans: {
premium: {
name: "Premium",
description: "Premium subscription plan with full access",
features: [
"Unlimited API calls",
"Priority support",
"Advanced analytics",
"Custom integrations",
"White-label options"
],
pricing: {
monthly: { price: 100, discount: 0 },
quarterly: { price: 270, discount: 10 },
annually: { price: 1000, discount: 17 }
},
billing: {
defaultPeriod: 'monthly',
allowPeriodChange: true,
prorationEnabled: true
}
},
basic: {
name: "Basic",
description: "Basic subscription plan",
features: [
"1000 API calls per month",
"Email support",
"Basic analytics",
"Standard integrations"
],
pricing: {
monthly: { price: 50, discount: 0 },
quarterly: { price: 135, discount: 10 },
annually: { price: 500, discount: 17 }
},
billing: {
defaultPeriod: 'monthly',
allowPeriodChange: true,
prorationEnabled: false
}
}
},
provider: {
rpcUrl: process.env.BASE_RPC_URL || "https://mainnet.base.org",
chainId: 8453,
explorer: 'https://basescan.org'
},
payment: {
autoApprove: true,
requireConfirmation: false,
refundPolicy: {
enabled: true,
gracePeriod: 7
},
billing: {
allowTrial: true,
trialDays: 7,
gracePeriodDays: 3
}
},
integration: {
webhookUrl: process.env.WEBHOOK_SUBSCRIPTION_CREATED,
redirectUrl: process.env.REDIRECT_URL,
successMessage: 'Subscription created successfully!',
errorMessage: 'Failed to create subscription. Please try again.'
},
analytics: {
trackRevenue: true,
trackUsage: true,
exportData: false
}
};
}
constructor(config) {
// Handle basic mode configuration
let finalConfig;
if (config.mode === 'basic') {
console.log("🚀 [EnhancedWeb3SubscriptionSkill] Initializing in BASIC mode with minimal configuration");
// Merge with default configuration
const defaultConfig = EnhancedWeb3SubscriptionSkill.getDefaultConfig(config.agentId);
finalConfig = {
...defaultConfig,
...config,
// Preserve user-provided values over defaults
agentId: config.agentId,
agentName: config.agentName || defaultConfig.agentName,
plans: config.plans || defaultConfig.plans
};
}
else {
// Advanced mode - use configuration as provided
console.log("🚀 [EnhancedWeb3SubscriptionSkill] Initializing in ADVANCED mode with full configuration");
finalConfig = config;
}
// Call super with the final configuration
super(finalConfig);
this.webhookQueue = [];
this.analyticsCache = new Map();
this.enhancedConfig = finalConfig;
// Log initialization for debugging
console.log("🚀 [EnhancedWeb3SubscriptionSkill] Initializing with config:", {
agentId: this.enhancedConfig.agentId,
agentName: this.enhancedConfig.agentName,
mode: this.enhancedConfig.mode || 'advanced',
hasPlans: !!this.enhancedConfig.plans && Object.keys(this.enhancedConfig.plans).length > 0,
planCount: this.enhancedConfig.plans ? Object.keys(this.enhancedConfig.plans).length : 0,
hasProvider: !!this.enhancedConfig.provider,
hasADaoToken: !!this.enhancedConfig.adaoToken
});
}
/**
* Check if the subscription skill is ready for use
*/
isReady() {
// Use public methods to check readiness
const hasSigner = this.hasSigner();
const hasPlans = this.hasPlans();
const hasProvider = this.hasProvider();
const hasADaoToken = this.hasADaoToken();
const ready = hasSigner && hasPlans && hasProvider && hasADaoToken;
if (!ready) {
console.warn("⚠️ [EnhancedWeb3SubscriptionSkill] Not ready:", {
hasSigner,
hasPlans,
hasProvider,
hasADaoToken
});
}
else {
console.log("✅ [EnhancedWeb3SubscriptionSkill] Ready for use");
}
return ready;
}
/**
* Check if signer is set
*/
hasSigner() {
try {
// Try to access signer through a public method or property
return !!this.signer;
}
catch {
return false;
}
}
/**
* Check if plans are configured
*/
hasPlans() {
try {
// Use the enhanced config which is public
return !!this.enhancedConfig.plans && Object.keys(this.enhancedConfig.plans).length > 0;
}
catch {
return false;
}
}
/**
* Check if provider is configured
*/
hasProvider() {
try {
return !!this.enhancedConfig.provider;
}
catch {
return false;
}
}
/**
* Check if ADAO token is configured
*/
hasADaoToken() {
try {
return !!this.enhancedConfig.adaoToken;
}
catch {
return false;
}
}
/**
* Get readiness status with details
*/
getReadinessStatus() {
return {
ready: this.isReady(),
hasSigner: this.hasSigner(),
hasPlans: this.hasPlans(),
hasProvider: this.hasProvider(),
hasADaoToken: this.hasADaoToken(),
planCount: this.enhancedConfig.plans ? Object.keys(this.enhancedConfig.plans).length : 0,
availablePlans: this.enhancedConfig.plans ? Object.keys(this.enhancedConfig.plans) : []
};
}
/**
* Validate plan exists before processing
*/
validatePlan(planId) {
if (!this.enhancedConfig.plans) {
console.error("❌ [EnhancedWeb3SubscriptionSkill] No plans configured. Please add plans to your configuration or use default plans.");
return false;
}
if (!this.enhancedConfig.plans[planId]) {
const availablePlans = Object.keys(this.enhancedConfig.plans);
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Plan '${planId}' not found. Available plans: ${availablePlans.join(', ')}. Please use one of the available plan IDs.`);
return false;
}
return true;
}
/**
* Validate billing period
*/
validateBillingPeriod(billingPeriod) {
const validPeriods = ['monthly', 'quarterly', 'annually'];
if (!validPeriods.includes(billingPeriod)) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Invalid billing period '${billingPeriod}'. Valid periods: ${validPeriods.join(', ')}. Please use one of the supported billing periods.`);
return false;
}
return true;
}
/**
* Validate user address format
*/
validateUserAddress(userAddress) {
if (!userAddress) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] User address is required. Please provide a valid Ethereum address.`);
return false;
}
if (!userAddress.startsWith('0x')) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Invalid Ethereum address: ${userAddress}. Address must start with '0x'. Example: 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6`);
return false;
}
if (userAddress.length !== 42) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Invalid Ethereum address length: ${userAddress}. Address must be exactly 42 characters (including '0x'). Example: 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6`);
return false;
}
// Additional validation using ethers.js
try {
if (!ethers_1.ethers.isAddress(userAddress)) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Invalid Ethereum address format: ${userAddress}. Please provide a valid 42-character address starting with 0x. Example: 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6`);
return false;
}
}
catch (error) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Address validation failed: ${userAddress}. Please check the address format. Example: 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6`);
return false;
}
return true;
}
/**
* Enhanced createSubscription with comprehensive validation
*/
async createSubscription(userAddress, planId, billingPeriod) {
// Pre-flight checks
if (!this.isReady()) {
throw new Error("Subscription skill not ready. Check readiness status.");
}
if (!this.validateUserAddress(userAddress)) {
throw new Error(`Invalid user address format: ${userAddress}`);
}
if (!this.validatePlan(planId)) {
throw new Error(`Plan '${planId}' not found. Available plans: ${Object.keys(this.enhancedConfig.plans).join(', ')}`);
}
if (!this.validateBillingPeriod(billingPeriod)) {
throw new Error(`Invalid billing period '${billingPeriod}'. Valid periods: monthly, quarterly, annually`);
}
// Check if plan supports the billing period
const plan = this.enhancedConfig.plans[planId];
if (!plan.pricing[billingPeriod]) {
throw new Error(`Plan '${planId}' does not support '${billingPeriod}' billing period`);
}
console.log(`[EnhancedWeb3SubscriptionSkill] Creating subscription:`, {
userAddress,
planId,
billingPeriod,
planName: plan.name
});
try {
const subscription = await super.createSubscription(userAddress, planId, billingPeriod);
console.log(`✅ [EnhancedWeb3SubscriptionSkill] Subscription created successfully:`, {
subscriptionId: subscription.id,
userAddress,
planId,
billingPeriod
});
return subscription;
}
catch (error) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Failed to create subscription:`, error);
throw error;
}
}
/**
* Enhanced checkSubscription with validation
*/
async checkSubscription(userAddress) {
if (!this.validateUserAddress(userAddress)) {
throw new Error(`Invalid user address format: ${userAddress}`);
}
try {
return await super.checkSubscription(userAddress);
}
catch (error) {
console.error(`❌ [EnhancedWeb3SubscriptionSkill] Failed to check subscription:`, error);
throw error;
}
}
/**
* Get health status with detailed feedback
*/
getHealthStatus() {
const readinessStatus = this.getReadinessStatus();
const missingConfig = [];
if (!readinessStatus.hasSigner) {
missingConfig.push('PLATFORM_PRIVATE_KEY environment variable');
}
if (!readinessStatus.hasProvider) {
missingConfig.push('BASE_RPC_URL environment variable');
}
if (!readinessStatus.hasADaoToken) {
missingConfig.push('ADAO_CONTRACT_ADDRESS environment variable');
}
if (!readinessStatus.hasPlans) {
missingConfig.push('Subscription plans configuration');
}
return {
timestamp: new Date().toISOString(),
skill: {
ready: this.isReady(),
hasSigner: this.hasSigner(),
hasPlans: this.hasPlans(),
hasProvider: this.hasProvider(),
hasADaoToken: this.hasADaoToken()
},
config: {
agentId: this.enhancedConfig.agentId,
agentName: this.enhancedConfig.agentName,
mode: this.enhancedConfig.mode || 'advanced',
planCount: this.enhancedConfig.plans ? Object.keys(this.enhancedConfig.plans).length : 0,
availablePlans: this.enhancedConfig.plans ? Object.keys(this.enhancedConfig.plans) : []
},
status: this.isReady() ? 'healthy' : 'unhealthy',
missingConfig,
recommendations: this.isReady() ? [] : [
'Set required environment variables',
'Configure subscription plans',
'Set up provider connection',
'Configure ADAO token contract'
]
};
}
// =====================
// BACKWARD COMPATIBLE METHODS (unchanged)
// =====================
// All existing methods work exactly the same
// createSubscription, checkSubscription, cancelSubscription, etc.
// are inherited from Web3SubscriptionSkill
// =====================
// NEW ENHANCED METHODS
// =====================
/**
* Async initialization method for better error handling
*/
async initialize() {
try {
console.log("🔄 [EnhancedWeb3SubscriptionSkill] Starting async initialization...");
// Check if already initialized
if (this.isReady()) {
console.log("✅ [EnhancedWeb3SubscriptionSkill] Already initialized and ready");
return true;
}
// Set up signer if available
if (process.env.PLATFORM_PRIVATE_KEY) {
try {
const providerUrl = this.enhancedConfig.provider?.rpcUrl || "https://mainnet.base.org";
const provider = new ethers_1.ethers.JsonRpcProvider(providerUrl);
const signer = new ethers_1.ethers.Wallet(process.env.PLATFORM_PRIVATE_KEY, provider);
this.setSigner(signer);
console.log("✅ [EnhancedWeb3SubscriptionSkill] Signer configured successfully");
}
catch (error) {
console.warn("⚠️ [EnhancedWeb3SubscriptionSkill] Failed to configure signer:", error);
}
}
// Wait a bit for everything to settle
await new Promise(resolve => setTimeout(resolve, 100));
const isReady = this.isReady();
if (isReady) {
console.log("✅ [EnhancedWeb3SubscriptionSkill] Initialization completed successfully");
}
else {
console.warn("⚠️ [EnhancedWeb3SubscriptionSkill] Initialization completed but skill is not ready");
const healthStatus = this.getHealthStatus();
console.warn("Missing configuration:", healthStatus.missingConfig);
}
return isReady;
}
catch (error) {
console.error("❌ [EnhancedWeb3SubscriptionSkill] Initialization failed:", error);
throw new Error(`Failed to initialize subscription skill: ${error instanceof Error ? error.message : 'Unknown error'}. Please check your configuration and try again.`);
}
}
/**
* Get available payment methods for a user across all chains
*/
async getPaymentMethods(userAddress) {
const methods = [];
for (const [tokenAddress, tokenConfig] of Object.entries(this.enhancedConfig.tokens || {})) {
try {
const provider = new ethers_1.ethers.JsonRpcProvider(this.enhancedConfig.chains?.[tokenConfig.chainId]?.rpcUrl);
if (tokenAddress === 'native') {
// Native token (ETH, MATIC, etc.)
const balance = await provider.getBalance(userAddress);
const balanceFormatted = parseFloat(ethers_1.ethers.formatEther(balance));
methods.push({
type: 'native',
address: 'native',
symbol: tokenConfig.symbol,
decimals: 18,
chainId: tokenConfig.chainId,
balance: balanceFormatted
});
}
else {
// ERC-20 token
const tokenContract = new ethers_1.ethers.Contract(tokenAddress, ['function balanceOf(address) view returns (uint256)'], provider);
const balance = await tokenContract.balanceOf(userAddress);
const balanceFormatted = parseFloat(ethers_1.ethers.formatUnits(balance, tokenConfig.decimals));
methods.push({
type: 'token',
address: tokenAddress,
symbol: tokenConfig.symbol,
decimals: tokenConfig.decimals,
chainId: tokenConfig.chainId,
balance: balanceFormatted
});
}
}
catch (error) {
console.warn(`Failed to get balance for token ${tokenAddress}:`, error);
}
}
return methods;
}
/**
* Create subscription with enhanced features
*/
async createEnhancedSubscription(userAddress, planId, billingPeriod, paymentMethod) {
// Create base subscription
const baseSubscription = await this.createSubscription(userAddress, planId, billingPeriod);
// Enhance with additional data
const enhancedSubscription = {
...baseSubscription,
paymentMethod,
chainId: paymentMethod.chainId,
analytics: {
totalPayments: 1,
totalRevenue: baseSubscription.price,
averageSessionDuration: 0,
featureUsage: {},
churnRate: 0,
lifetimeValue: baseSubscription.price
}
};
// Send webhook
await this.sendWebhook('subscriptionCreated', {
subscriptionId: enhancedSubscription.id,
userAddress,
planId,
billingPeriod,
paymentMethod,
amount: baseSubscription.price
});
// Track analytics
await this.trackAnalytics('subscription_created', {
userAddress,
planId,
amount: baseSubscription.price,
paymentMethod: paymentMethod.symbol
});
return enhancedSubscription;
}
/**
* Upgrade subscription to a higher plan
*/
async upgradeSubscription(userAddress, newPlanId, newPeriod) {
const currentStatus = await this.checkSubscription(userAddress);
if (!currentStatus.hasActiveSubscription) {
throw new Error('No active subscription found');
}
const currentPlan = this.enhancedConfig.plans[currentStatus.planId];
const newPlan = this.enhancedConfig.plans[newPlanId];
if (!newPlan) {
throw new Error(`Plan ${newPlanId} not found`);
}
// Calculate prorated amount
const proratedAmount = await this.calculateProratedUpgrade(userAddress, currentStatus.planId, newPlanId, newPeriod || currentStatus.billingPeriod);
// Create upgrade record
const upgradeRecord = {
id: `upgrade-${Date.now()}`,
fromPlan: currentStatus.planId,
toPlan: newPlanId,
fromPeriod: currentStatus.billingPeriod,
toPeriod: newPeriod || currentStatus.billingPeriod,
proratedAmount,
timestamp: new Date()
};
// Process the upgrade (this would interact with smart contracts)
console.log(`Upgrading subscription for user: ${userAddress}`);
console.log(`From: ${currentStatus.planId} to: ${newPlanId}`);
console.log(`Prorated amount: ${proratedAmount}`);
// Send webhook
await this.sendWebhook('subscriptionUpgraded', {
userAddress,
fromPlan: currentStatus.planId,
toPlan: newPlanId,
proratedAmount
});
// Return enhanced subscription
return {
id: `upgraded-${Date.now()}`,
userAddress,
planId: newPlanId,
planName: newPlan.name,
billingPeriod: newPeriod || currentStatus.billingPeriod,
price: proratedAmount,
monthlyPrice: this.calculateEnhancedMonthlyPrice(proratedAmount, newPeriod || currentStatus.billingPeriod),
startDate: new Date(),
endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
status: 'active',
features: newPlan.features,
discountApplied: 0,
paymentMethod: await this.getDefaultPaymentMethod(userAddress),
chainId: 8453, // Default to Base
upgradeHistory: [upgradeRecord]
};
}
/**
* Downgrade subscription to a lower plan
*/
async downgradeSubscription(userAddress, newPlanId, newPeriod) {
const currentStatus = await this.checkSubscription(userAddress);
if (!currentStatus.hasActiveSubscription) {
throw new Error('No active subscription found');
}
const newPlan = this.enhancedConfig.plans[newPlanId];
if (!newPlan) {
throw new Error(`Plan ${newPlanId} not found`);
}
// Calculate credit for downgrade
const creditAmount = await this.calculateDowngradeCredit(userAddress, currentStatus.planId, newPlanId, newPeriod || currentStatus.billingPeriod);
// Process the downgrade
console.log(`Downgrading subscription for user: ${userAddress}`);
console.log(`To: ${newPlanId}, Credit: ${creditAmount}`);
// Send webhook
await this.sendWebhook('subscriptionDowngraded', {
userAddress,
toPlan: newPlanId,
creditAmount
});
return {
id: `downgraded-${Date.now()}`,
userAddress,
planId: newPlanId,
planName: newPlan.name,
billingPeriod: newPeriod || currentStatus.billingPeriod,
price: 0, // No charge for downgrade
monthlyPrice: 0,
startDate: new Date(),
endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
status: 'active',
features: newPlan.features,
discountApplied: 0,
paymentMethod: await this.getDefaultPaymentMethod(userAddress),
chainId: 8453
};
}
/**
* Process refund for a subscription
*/
async processRefund(userAddress, amount, reason) {
if (!this.enhancedConfig.refunds?.enabled) {
throw new Error('Refunds are not enabled for this subscription');
}
const currentStatus = await this.checkSubscription(userAddress);
if (!currentStatus.hasActiveSubscription) {
throw new Error('No active subscription found');
}
const refundRecord = {
id: `refund-${Date.now()}`,
amount,
reason,
status: this.enhancedConfig.refunds.requireApproval ? 'pending' : 'approved',
timestamp: new Date()
};
// If auto-refund is enabled, process immediately
if (this.enhancedConfig.refunds.autoRefund && !this.enhancedConfig.refunds.requireApproval) {
refundRecord.status = 'processed';
// Here you would interact with smart contracts to process the refund
console.log(`Processing refund of ${amount} for user: ${userAddress}`);
}
// Send webhook
await this.sendWebhook('refundIssued', {
userAddress,
amount,
reason,
status: refundRecord.status
});
return refundRecord;
}
/**
* Get enhanced analytics for a subscription
*/
async getEnhancedAnalytics(userAddress) {
const cached = this.analyticsCache.get(userAddress);
if (cached) {
return cached;
}
const subscription = await this.checkSubscription(userAddress);
if (!subscription.hasActiveSubscription) {
throw new Error('No active subscription found');
}
// Mock analytics data (in real implementation, this would come from database)
const analytics = {
totalPayments: 1,
totalRevenue: 100, // Mock data
averageSessionDuration: 45, // minutes
featureUsage: {
chat: 150,
analytics: 25,
support: 10
},
churnRate: 0.05, // 5%
lifetimeValue: 100
};
this.analyticsCache.set(userAddress, analytics);
return analytics;
}
/**
* Get enhanced revenue statistics
*/
async getEnhancedRevenueStats() {
const baseStats = await this.getRevenueStats();
// Mock enhanced data (in real implementation, this would come from database)
const enhancedStats = {
...baseStats,
byChain: {
8453: baseStats.totalRevenue * 0.8, // 80% on Base
137: baseStats.totalRevenue * 0.2 // 20% on Polygon
},
byToken: {
[this.enhancedConfig.adaoToken.address]: baseStats.totalRevenue * 0.7, // 70% ADAO
'0x0000000000000000000000000000000000000000': baseStats.totalRevenue * 0.3 // 30% native
},
byPeriod: {
monthly: baseStats.totalRevenue * 0.4,
quarterly: baseStats.totalRevenue * 0.35,
annually: baseStats.totalRevenue * 0.25
},
churnRate: 0.05,
averageLifetimeValue: 250,
refundRate: 0.02,
upgradeRate: 0.15
};
return enhancedStats;
}
// =====================
// PRIVATE HELPER METHODS
// =====================
async sendWebhook(event, data) {
const webhookUrl = this.enhancedConfig.webhooks?.[event];
if (!webhookUrl)
return;
const payload = {
event,
subscriptionId: data.subscriptionId || 'unknown',
userAddress: data.userAddress,
data,
timestamp: new Date()
};
try {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'AgentDAO-Web3Subscription/1.0'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
console.warn(`Webhook failed for event ${event}:`, response.statusText);
// Queue for retry
this.webhookQueue.push(payload);
}
}
catch (error) {
console.error(`Webhook error for event ${event}:`, error);
this.webhookQueue.push(payload);
}
}
async trackAnalytics(event, data) {
if (!this.enhancedConfig.enhancedAnalytics?.trackUsage)
return;
try {
// Send to analytics endpoint
await fetch('/api/analytics/track', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event,
agentId: this.enhancedConfig.agentId,
data,
timestamp: new Date().toISOString()
})
});
}
catch (error) {
console.error('Analytics tracking failed:', error);
}
}
async getDefaultPaymentMethod(userAddress) {
const methods = await this.getPaymentMethods(userAddress);
return methods[0] || {
type: 'token',
address: this.enhancedConfig.adaoToken.address,
symbol: 'ADAO',
decimals: this.enhancedConfig.adaoToken.decimals,
chainId: 8453,
balance: 0
};
}
async calculateProratedUpgrade(userAddress, fromPlanId, toPlanId, newPeriod) {
const fromPlan = this.enhancedConfig.plans[fromPlanId];
const toPlan = this.enhancedConfig.plans[toPlanId];
if (!fromPlan || !toPlan) {
throw new Error('Invalid plan configuration');
}
const fromPrice = fromPlan.pricing[newPeriod].price;
const toPrice = toPlan.pricing[newPeriod].price;
// Simple proration: difference between plans
return Math.max(0, toPrice - fromPrice);
}
async calculateDowngradeCredit(userAddress, fromPlanId, toPlanId, newPeriod) {
const fromPlan = this.enhancedConfig.plans[fromPlanId];
const toPlan = this.enhancedConfig.plans[toPlanId];
if (!fromPlan || !toPlan) {
throw new Error('Invalid plan configuration');
}
const fromPrice = fromPlan.pricing[newPeriod].price;
const toPrice = toPlan.pricing[newPeriod].price;
// Credit for downgrade
return Math.max(0, fromPrice - toPrice);
}
calculateEnhancedMonthlyPrice(price, billingPeriod) {
switch (billingPeriod) {
case 'monthly':
return price;
case 'quarterly':
return price / 3;
case 'annually':
return price / 12;
default:
return price;
}
}
}
exports.EnhancedWeb3SubscriptionSkill = EnhancedWeb3SubscriptionSkill;