billing

package
v0.2.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 13, 2025 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var CancelCommand = &cli.Command{
	Name:  "cancel",
	Usage: "Cancel subscription",
	Flags: append(common.GlobalFlags, []cli.Flag{
		common.EnvironmentFlag,
	}...),
	Action: func(cCtx *cli.Context) error {
		ctx := cCtx.Context
		logger := common.LoggerFromContext(cCtx)
		environmentConfig, err := utils.GetEnvironmentConfig(cCtx)
		if err != nil {
			return fmt.Errorf("failed to get environment config: %w", err)
		}
		envName := environmentConfig.Name

		if _, err := utils.GetPrivateKeyOrFail(cCtx); err != nil {
			return err
		}

		apiClient, err := utils.NewUserApiClient(cCtx)
		if err != nil {
			return fmt.Errorf("failed to create API client: %w", err)
		}

		subscription, err := apiClient.GetUserSubscription(cCtx)
		if err != nil {
			return fmt.Errorf("failed to check subscription status: %w", err)
		}

		if !isSubscriptionActive(subscription.Status) {
			logger.Info("You don't have an active subscription on %s.", envName)
			return nil
		}

		caller, err := utils.GetContractCaller(cCtx)
		if err != nil {
			return fmt.Errorf("failed to get contract caller: %w", err)
		}

		developerAddr, err := utils.GetDeveloperAddress(cCtx)
		if err != nil {
			return fmt.Errorf("failed to get developer address: %w", err)
		}

		activeAppCount, err := caller.GetActiveAppCount(ctx, developerAddr)
		if err != nil {
			return fmt.Errorf("failed to get active app count: %w", err)
		}

		if activeAppCount > 0 {
			logger.Info("You have %d active app(s) on %s that will be suspended.", activeAppCount, envName)
			logger.Info("")

			confirmed, err := output.Confirm("Continue?")
			if err != nil {
				return fmt.Errorf("failed to get confirmation: %w", err)
			}

			if !confirmed {
				logger.Info("Cancellation aborted.")
				return nil
			}

			activeApps, err := getActiveAppsByCreator(ctx, caller, developerAddr)
			if err != nil {
				return fmt.Errorf("failed to get active apps: %w", err)
			}

			if len(activeApps) > 0 {
				logger.Info("Suspending apps...")

				err = caller.Suspend(ctx, developerAddr, activeApps)
				if err != nil {
					return fmt.Errorf("failed to suspend apps: %w", err)
				}

				logger.Info("✓ Apps suspended")
			}
		} else {

			logger.Warn("Canceling your subscription on %s will prevent you from deploying new apps.", envName)
			confirmed, err := output.Confirm("Are you sure you want to cancel your subscription?")
			if err != nil {
				return fmt.Errorf("failed to get confirmation: %w", err)
			}

			if !confirmed {
				logger.Info("Cancellation aborted.")
				return nil
			}
		}

		logger.Info("Canceling subscription...")
		if err := apiClient.CancelSubscription(cCtx); err != nil {
			return fmt.Errorf("failed to cancel subscription: %w", err)
		}

		logger.Info("\n✓ Subscription canceled successfully for %s.", envName)
		return nil
	},
}
View Source
var StatusCommand = &cli.Command{
	Name:  "status",
	Usage: "Show subscription status and usage",
	Flags: append(common.GlobalFlags, []cli.Flag{
		common.EnvironmentFlag,
	}...),
	Action: func(cCtx *cli.Context) error {
		logger := common.LoggerFromContext(cCtx)
		environmentConfig, err := utils.GetEnvironmentConfig(cCtx)
		if err != nil {
			return fmt.Errorf("failed to get environment config: %w", err)
		}
		envName := environmentConfig.Name

		if _, err := utils.GetPrivateKeyOrFail(cCtx); err != nil {
			return err
		}

		apiClient, err := utils.NewUserApiClient(cCtx)
		if err != nil {
			return fmt.Errorf("failed to create API client: %w", err)
		}

		subscription, err := apiClient.GetUserSubscription(cCtx)
		if err != nil {
			return fmt.Errorf("failed to get subscription details: %w", err)
		}

		hasCurrentPeriodEnd := subscription.CurrentPeriodEnd != nil && *subscription.CurrentPeriodEnd > 0
		dateFormat := "January 2, 2006"

		fmt.Println()

		statusDisplay := formatStatus(subscription.Status)
		logger.Info("Status: %s", statusDisplay)

		if subscription.Status == utils.StatusCanceled {
			logger.Info("\nYour subscription has been canceled.")
			if hasCurrentPeriodEnd {
				endDate := time.Unix(*subscription.CurrentPeriodEnd, 0)
				logger.Info("Access ended on %s.", endDate.Format(dateFormat))
			}
			logger.Info("Run 'eigenx billing subscribe' to resubscribe.")

			if subscription.PortalURL != nil && *subscription.PortalURL != "" {
				logger.Info("\nView invoices and billing history:")
				logger.Info("  %s", *subscription.PortalURL)
			}

			return nil
		}

		if subscription.Status == utils.StatusPastDue || subscription.Status == utils.StatusUnpaid {
			logger.Warn("\nYour subscription has a payment issue.")
			logger.Info("Please update your payment method to restore access.")

			if subscription.PortalURL != nil && *subscription.PortalURL != "" {
				logger.Info("\nUpdate payment method:")
				logger.Info("  %s", *subscription.PortalURL)
			}

			return nil
		}

		if !isSubscriptionActive(subscription.Status) {
			logger.Info("\nYou don't have an active subscription on %s.", envName)
			logger.Info("Run 'eigenx billing subscribe' to get started.")
			return nil
		}

		if subscription.PlanPrice != nil && subscription.Currency != nil && *subscription.PlanPrice > 0 {
			logger.Info("Plan: $%.2f/month", *subscription.PlanPrice)
		} else {
			logger.Info("Plan: Standard")
		}
		logger.Info("")

		logger.Info("Usage:")

		caller, err := utils.GetContractCaller(cCtx)
		if err != nil {
			logger.Warn("  Unable to fetch usage: %v", err)
			logger.Info("")
		} else if developerAddr, err := utils.GetDeveloperAddress(cCtx); err != nil {
			logger.Warn("  Unable to fetch usage: failed to get developer address")
			logger.Info("")
		} else if count, err := caller.GetActiveAppCount(cCtx.Context, developerAddr); err != nil {
			logger.Warn("  Unable to fetch usage: %v", err)
			logger.Info("")
		} else {
			logger.Info("  %d / 1 apps deployed on %s", count, envName)
			logger.Info("")
		}

		logger.Info("Billing:")

		if subscription.UpcomingInvoice != nil && subscription.UpcomingInvoice.Date > 0 {
			nextBilling := time.Unix(subscription.UpcomingInvoice.Date, 0)
			logger.Info("  Next charge: $%.2f on %s", subscription.UpcomingInvoice.Amount, nextBilling.Format(dateFormat))
		} else if hasCurrentPeriodEnd {
			nextBilling := time.Unix(*subscription.CurrentPeriodEnd, 0)
			logger.Info("  Next billing: %s", nextBilling.Format(dateFormat))
		}

		if subscription.CancelAtPeriodEnd != nil && *subscription.CancelAtPeriodEnd {
			if hasCurrentPeriodEnd {
				endDate := time.Unix(*subscription.CurrentPeriodEnd, 0)
				logger.Info("  ⚠ Scheduled for cancellation on %s", endDate.Format(dateFormat))
			}
		}

		logger.Info("")

		if subscription.PortalURL != nil && *subscription.PortalURL != "" {
			logger.Info("Manage billing online:")
			logger.Info("  %s", *subscription.PortalURL)
			logger.Info("")
		}
		return nil
	},
}
View Source
var SubscribeCommand = &cli.Command{
	Name:  "subscribe",
	Usage: "Subscribe to start deploying apps",
	Flags: append(common.GlobalFlags, []cli.Flag{
		common.EnvironmentFlag,
	}...),
	Action: func(cCtx *cli.Context) error {
		logger := common.LoggerFromContext(cCtx)
		environmentConfig, err := utils.GetEnvironmentConfig(cCtx)
		if err != nil {
			return fmt.Errorf("failed to get environment config: %w", err)
		}
		envName := environmentConfig.Name

		if _, err := utils.GetPrivateKeyOrFail(cCtx); err != nil {
			return err
		}

		client, err := utils.NewUserApiClient(cCtx)
		if err != nil {
			return fmt.Errorf("failed to create API client: %w", err)
		}

		subscription, err := client.GetUserSubscription(cCtx)
		if err != nil {
			return fmt.Errorf("failed to check subscription status: %w", err)
		}

		if isSubscriptionActive(subscription.Status) {
			logger.Info("You're already subscribed to %s. Run 'eigenx billing status' for details.", envName)
			return nil
		}

		if subscription.Status == utils.StatusPastDue || subscription.Status == utils.StatusUnpaid {
			logger.Info("You already have a subscription on %s, but it has a payment issue.", envName)
			logger.Info("Please update your payment method to restore access.")

			if subscription.PortalURL != nil && *subscription.PortalURL != "" {
				logger.Info("\nUpdate payment method:")
				logger.Info("  %s", *subscription.PortalURL)
			}

			return nil
		}

		logger.Info("Creating checkout session for %s...", envName)
		session, err := client.CreateCheckoutSession(cCtx)
		if err != nil {
			return fmt.Errorf("failed to create checkout session: %w", err)
		}

		logger.Info("Opening payment page in your browser...")
		if err := browser.OpenURL(session.CheckoutURL); err != nil {
			logger.Warn("Failed to open browser automatically: %v", err)
			logger.Info("\nPlease open this URL in your browser:")
			logger.Info(session.CheckoutURL)
		}

		logger.Info("\nWaiting for payment completion...")
		timeout := time.After(5 * time.Minute)
		ticker := time.NewTicker(3 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case <-timeout:
				return fmt.Errorf("payment confirmation timed out after 5 minutes. If you completed payment, run 'eigenx billing status' to check status")
			case <-ticker.C:
				subscription, err := client.GetUserSubscription(cCtx)
				if err != nil {
					logger.Debug("Failed to check subscription status: %v", err)
					continue
				}

				if isSubscriptionActive(subscription.Status) {
					logger.Info("\n✓ Subscription activated successfully for %s!", envName)
					logger.Info("\nYou now have access to deploy 1 app on %s", envName)
					logger.Info("\nStart deploying with: eigenx app deploy")
					return nil
				}
			}
		}
	},
}

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL