Skip to content

Conversation

@solimander
Copy link
Collaborator

Summary

  • Add eigenx billing commands for Stripe-based subscription management
  • Add billing API client with environment-specific auth
  • Enforce quota checks during app deployment

Changes

  • New commands: billing subscribe, billing cancel, billing status
  • Deploy now checks available quota before proceeding
  • Browser-based Stripe checkout flow for subscriptions

@claude
Copy link

claude bot commented Oct 27, 2025

Claude finished @solimander's task —— View job


PR Review: Stripe-based Billing Subscription Management

Todo List:

  • Read repository conventions from CLAUDE.md
  • Review billing command structure and implementation
  • Analyze security considerations for Stripe integration
  • Check error handling and user experience
  • Evaluate code quality and best practices
  • Look for potential performance issues
  • Assess test coverage needs
  • Provide comprehensive feedback

Code Review Summary

This PR successfully adds Stripe-based billing subscription management to the eigenx CLI. The implementation is well-structured and follows Go best practices. Here's my detailed analysis:

Strengths

  1. Clean Architecture: Commands are properly organized under pkg/commands/billing/ following the existing pattern
  2. Security-First Design: Uses cryptographic authentication with KMS keys for API calls
  3. User Experience: Good progressive disclosure with helpful error messages and billing portal links
  4. Environment Awareness: Properly integrates with existing environment configuration system
  5. Quota Integration: Deployment process correctly checks billing status before proceeding

🔍 Code Quality & Best Practices

Good Practices:

  • Consistent error handling with wrapped errors (fmt.Errorf)
  • Proper context propagation throughout the call chain
  • Good separation of concerns between commands and API client
  • Follows existing CLI patterns with urfave/cli/v2

Suggestions for Improvement:

pkg/commands/billing/subscribe.go:73-80

The 5-minute timeout for payment polling is reasonable, but consider adding a progress indicator:

logger.Info("Waiting for payment completion... (timeout in 5 minutes)")

pkg/commands/billing/status.go:87-91

The price display logic could be more robust:

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

Consider handling different currencies and showing the currency symbol.

🔒 Security Analysis

Excellent Security Practices:

  • All billing API calls require CanManageBillingPermission
  • Cryptographic authentication using KMS keys
  • No sensitive data (API keys, tokens) stored locally
  • Browser-based checkout prevents CLI from handling payment details
  • Proper permission-based access control

No security issues found - the implementation follows security best practices throughout.

🚀 Performance Considerations

Good Performance Characteristics:

  • 30-second HTTP client timeout prevents hanging
  • Efficient polling with 3-second intervals
  • Proper error handling prevents unnecessary retries
  • Contract calls are minimal and cached where appropriate

🧪 Test Coverage Recommendations

The implementation would benefit from:

  1. Unit tests for status formatting:

    func TestFormatStatus(t *testing.T) {
        assert.Equal(t, "✓ Active", formatStatus(utils.StatusActive))
        assert.Equal(t, "⚠ Past Due", formatStatus(utils.StatusPastDue))
    }
  2. Integration tests for quota checking:

    • Test deployment blocking when quota exceeded
    • Test subscription activation enabling deployment
  3. Error handling tests:

    • API failures, network timeouts
    • Invalid subscription states

🐛 Potential Issues

Minor Issues:

  1. pkg/commands/billing/status.go:102-104: The chained error handling could be cleaner:

    if developerAddr, err := utils.GetDeveloperAddress(cCtx); err != nil {
        logger.Warn("  Unable to fetch usage: failed to get developer address")
    } else if count, err := caller.GetActiveAppCount(cCtx.Context, developerAddr); err != nil {
        logger.Warn("  Unable to fetch usage: %v", err)  
    } else {
        logger.Info("  %d / 1 apps deployed on %s", count, envName)
    }
  2. pkg/commands/billing/cancel.go:121-139: The getActiveAppsByCreator function fetches up to 1,000 apps which could be slow for users with many apps. Consider pagination or filtering at the contract level.

User Experience Excellence

Outstanding UX Features:

  • Clear status indicators with emojis (✓, ⚠, ✗)
  • Helpful guidance for next steps in each state
  • Graceful handling of payment issues with portal links
  • App suspension warnings before cancellation
  • Browser auto-opening with fallback URL display

📋 Integration Quality

The billing integration seamlessly fits into the existing codebase:

  • Follows established patterns in pkg/commands/app/
  • Reuses existing utilities and configurations
  • Properly integrates with contract caller system
  • Maintains consistency with other commands

Overall Assessment

This is a high-quality implementation that successfully adds billing functionality while maintaining the CLI's existing patterns and security standards. The code is production-ready with excellent error handling and user experience.

Recommended Actions:

  1. Add unit tests for status formatting and error scenarios
  2. Consider adding integration tests for quota enforcement
  3. Minor code cleanup for chained error handling

The implementation demonstrates strong engineering practices and security awareness. Great work! 🎉


@solimander solimander changed the title feat: add Stripe-based billing subscription management feat: add Stripe-based billing subscription management Oct 27, 2025

// Poll for subscription activation
logger.Info("\nWaiting for payment completion...")
timeout := time.After(5 * time.Minute)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why timeout? just allow them to ctrl+C

Copy link
Collaborator Author

@solimander solimander Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't seem worth continuing to poll after 5 mins or so. they can also view the billing status via eigenx billing status if it stops watching

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool. will check when i go through it myself


for env, info := range results {
if info.Count > 0 {
networkAppCounts[env] = info.Count
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont u already have this in results?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@solimander solimander marked this pull request as ready for review October 28, 2025 14:46

// Plan details
if subscription.PlanPrice != nil && subscription.Currency != nil && *subscription.PlanPrice > 0 {
logger.Info("Plan: $%.2f %s", *subscription.PlanPrice, strings.ToUpper(*subscription.Currency))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need $ and currency?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// Cancellation status
if subscription.CancelAtPeriodEnd != nil && *subscription.CancelAtPeriodEnd {
if subscription.CurrentPeriodEnd != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another &&?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else {
logger.Info(" %d / 1 apps deployed on %s", count, envName)
logger.Info("")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just split up into if err != nils?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if/else feels cleaner given we pass-through imo

- Refactored subscription management to handle subscriptions per environment instead of globally
- Updated status command to show subscription details and app usage for current environment only
- Modified cancel command to suspend apps only on current environment when canceling
- Added improved handling of different subscription states (past_due, unpaid, etc.) with portal links
- Enhanced status display formatting with clearer subscription
@solimander solimander merged commit 1cb3c80 into main Nov 4, 2025
12 checks passed
@solimander solimander deleted the soli/billing-mmvp branch November 4, 2025 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants