Mauricio Araujo 2 years ago
parent
commit
51c1dd389f

+ 1 - 1
api/server/shared/apitest/config.go

@@ -60,7 +60,7 @@ func (t *TestConfigLoader) LoadConfig() (*config.Config, error) {
 		UserNotifier:       notifier,
 		LaunchDarklyClient: &features.Client{},
 		AnalyticsClient:    analytics.InitializeAnalyticsSegmentClient("", l),
-		BillingManager:     &billing.NoopBillingManager{},
+		BillingManager:     billing.BillingManager{},
 		TelemetryConfig:    telemetry.TracerConfig{ServiceName: "fake", CollectorURL: "fake"},
 	}, nil
 }

+ 5 - 0
api/types/billing.go

@@ -62,6 +62,7 @@ type EndCustomerPlanRequest struct {
 	VoidStripeInvoices bool   `json:"void_stripe_invoices"`    // Will void Stripe invoices if VoidInvoices is set to true. Drafts will be deleted.
 }
 
+// ListCreditGrantsRequest is the request to list a user's credit grants
 type ListCreditGrantsRequest struct {
 	// An array of credit type IDs. This must not be specified if
 	// credit_grant_ids is specified.
@@ -79,15 +80,18 @@ type ListCreditGrantsRequest struct {
 	EffectiveBefore string `json:"effective_before,omitempty"`
 }
 
+// ListCreditGrantsResponse is the response returned by the list credit grants request
 type ListCreditGrantsResponse struct {
 	Data []CreditGrant `json:"data"`
 }
 
+// CreditType is the type of the credit used in the credit grant
 type CreditType struct {
 	Name string `json:"name"` // The name of the credit type
 	ID   string `json:"id"`   // The UUID of the credit type
 }
 
+// GrantAmount represents the amount of credits granted
 type GrantAmount struct {
 	Amount     int64      `json:"amount"`      // The amount of credits granted
 	CreditType CreditType `json:"credit_type"` // The credit type for the amount granted
@@ -101,6 +105,7 @@ type Balance struct {
 	EffectiveAt      string `json:"effective_at"`      // The end date of the customer's current billing period in RFC 3339 format.
 }
 
+// CreditGrant is a grant given to a specific user on a specific plan
 type CreditGrant struct {
 	ID          uuid.UUID `json:"id"`
 	Name        string

+ 2 - 80
internal/billing/billing.go

@@ -1,85 +1,7 @@
 package billing
 
-<<<<<<< HEAD
-import (
-	"context"
-
-	"github.com/porter-dev/porter/api/types"
-)
-
-// BillingManager contains methods for managing billing for a project
-type BillingManager interface {
-	// CreateCustomer registers a project in the billing provider. This is currently a one-to-one
-	// mapping with projects and billing customers, because billing and usage are set per project.
-	CreateCustomer(ctx context.Context, userEmail string, projectID uint, projectName string) (customerID string, err error)
-
-	// DeleteCustomer will delete the customer from the billing provider
-	DeleteCustomer(ctx context.Context, customerID string) (err error)
-
-	// CheckPaymentEnabled will check if the project has a payment method configured
-	CheckPaymentEnabled(ctx context.Context, customerID string) (paymentEnabled bool, err error)
-
-	// ListPaymentMethod will return all payment methods for the project
-	ListPaymentMethod(ctx context.Context, customerID string) (paymentMethods []types.PaymentMethod, err error)
-
-	// CreatePaymentMethod will add a new payment method to the project in Stripe
-	CreatePaymentMethod(ctx context.Context, customerID string) (clientSecret string, err error)
-
-	// SetDefaultPaymentMethod will set the payment method as default in the customer invoice settings
-	SetDefaultPaymentMethod(ctx context.Context, paymentMethodID string, customerID string) (err error)
-
-	// DeletePaymentMethod will remove a payment method for the project in Stripe
-	DeletePaymentMethod(ctx context.Context, paymentMethodID string) (err error)
-
-	// GetPublishableKey returns the key used to render frontend components for the billing manager
-	GetPublishableKey(ctx context.Context) (key string)
-}
-
-// NoopBillingManager performs no billing operations
-type NoopBillingManager struct{}
-
-// CreateCustomer is a no-op
-func (s *NoopBillingManager) CreateCustomer(ctx context.Context, userEmail string, projectID uint, projectName string) (customerID string, err error) {
-	return "", nil
-}
-
-// DeleteCustomer is a no-op
-func (s *NoopBillingManager) DeleteCustomer(ctx context.Context, customerID string) (err error) {
-	return nil
-}
-
-// CheckPaymentEnabled is a  no-op
-func (s *NoopBillingManager) CheckPaymentEnabled(ctx context.Context, customerID string) (paymentEnabled bool, err error) {
-	return false, nil
-}
-
-// ListPaymentMethod is a no-op
-func (s *NoopBillingManager) ListPaymentMethod(ctx context.Context, customerID string) (paymentMethods []types.PaymentMethod, err error) {
-	return []types.PaymentMethod{}, nil
-}
-
-// CreatePaymentMethod is a no-op
-func (s *NoopBillingManager) CreatePaymentMethod(ctx context.Context, customerID string) (clientSecret string, err error) {
-	return "", nil
-}
-
-// SetDefaultPaymentMethod is a no-op
-func (s *NoopBillingManager) SetDefaultPaymentMethod(ctx context.Context, paymentMethodID string, customerID string) (err error) {
-	return nil
-}
-
-// DeletePaymentMethod is a no-op
-func (s *NoopBillingManager) DeletePaymentMethod(ctx context.Context, paymentMethodID string) (err error) {
-	return nil
-}
-
-// GetPublishableKey is a no-op
-func (s *NoopBillingManager) GetPublishableKey(ctx context.Context) (key string) {
-	return ""
-=======
-// BillingManager contains methods for managing billing for a project
-type BillingManager struct {
+// Manager contains methods for managing billing for a project
+type Manager struct {
 	StripeClient    *StripeClient
 	MetronomeClient *MetronomeClient
->>>>>>> b8c4273a5 (Add Metronome business logic)
 }

+ 13 - 7
internal/billing/metronome.go

@@ -21,16 +21,19 @@ const (
 	defaultGrantExpiryMonths = 1
 )
 
+// MetronomeClient is the client used to call the Metronome API
 type MetronomeClient struct {
 	ApiKey string
 }
 
+// NewMetronomeClient returns a new Metronome client
 func NewMetronomeClient(metronomeApiKey string) *MetronomeClient {
 	return &MetronomeClient{
 		ApiKey: metronomeApiKey,
 	}
 }
 
+// CreateCustomer will create the customer in Metronome
 func (m *MetronomeClient) CreateCustomer(orgName string, projectName string, projectID uint, billingID string) (customerID uuid.UUID, err error) {
 	path := "customers"
 	projIDStr := strconv.FormatUint(uint64(projectID), 10)
@@ -51,13 +54,14 @@ func (m *MetronomeClient) CreateCustomer(orgName string, projectName string, pro
 		Data types.Customer `json:"data"`
 	}
 
-	err = post(path, http.MethodPost, m.ApiKey, customer, &result)
+	err = post(path, m.ApiKey, customer, &result)
 	if err != nil {
 		return customerID, err
 	}
 	return result.Data.ID, nil
 }
 
+// AddCustomerPlan will start the customer on the given plan
 func (m *MetronomeClient) AddCustomerPlan(customerID uuid.UUID, planID uuid.UUID) (customerPlanID uuid.UUID, err error) {
 	if customerID == uuid.Nil || planID == uuid.Nil {
 		return customerPlanID, fmt.Errorf("customer or plan id empty")
@@ -77,7 +81,7 @@ func (m *MetronomeClient) AddCustomerPlan(customerID uuid.UUID, planID uuid.UUID
 
 	var result types.AddCustomerPlanResponse
 
-	err = post(path, http.MethodPost, m.ApiKey, req, &result)
+	err = post(path, m.ApiKey, req, &result)
 	if err != nil {
 		return customerPlanID, err
 	}
@@ -85,6 +89,7 @@ func (m *MetronomeClient) AddCustomerPlan(customerID uuid.UUID, planID uuid.UUID
 	return result.Data.CustomerPlanID, nil
 }
 
+// EndCustomerPlan will immediately end the plan for the given customer
 func (m *MetronomeClient) EndCustomerPlan(customerID uuid.UUID, customerPlanID uuid.UUID) (err error) {
 	if customerID == uuid.Nil || customerPlanID == uuid.Nil {
 		return fmt.Errorf("customer or customer plan id empty")
@@ -101,7 +106,7 @@ func (m *MetronomeClient) EndCustomerPlan(customerID uuid.UUID, customerPlanID u
 		EndingBefore: endBefore,
 	}
 
-	err = post(path, http.MethodPost, m.ApiKey, req, nil)
+	err = post(path, m.ApiKey, req, nil)
 	if err != nil {
 		return err
 	}
@@ -109,6 +114,7 @@ func (m *MetronomeClient) EndCustomerPlan(customerID uuid.UUID, customerPlanID u
 	return nil
 }
 
+// GetCustomerCredits will return the first credit grant for the customer
 func (m *MetronomeClient) GetCustomerCredits(customerID uuid.UUID) (credits int64, err error) {
 	if customerID == uuid.Nil {
 		return credits, fmt.Errorf("customer id empty")
@@ -123,7 +129,7 @@ func (m *MetronomeClient) GetCustomerCredits(customerID uuid.UUID) (credits int6
 	}
 
 	var result types.ListCreditGrantsResponse
-	err = post(path, http.MethodPost, m.ApiKey, req, &result)
+	err = post(path, m.ApiKey, req, &result)
 	if err != nil {
 		return credits, err
 	}
@@ -131,7 +137,7 @@ func (m *MetronomeClient) GetCustomerCredits(customerID uuid.UUID) (credits int6
 	return result.Data[0].Balance.IncludingPending, nil
 }
 
-func post(path string, method string, apiKey string, body interface{}, data interface{}) (err error) {
+func post(path string, apiKey string, body interface{}, data interface{}) (err error) {
 	client := http.Client{}
 	endpoint, err := url.JoinPath(metronomeBaseUrl, path)
 	if err != nil {
@@ -146,7 +152,7 @@ func post(path string, method string, apiKey string, body interface{}, data inte
 		}
 	}
 
-	req, err := http.NewRequest(method, endpoint, bytes.NewBuffer(bodyJson))
+	req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(bodyJson))
 	if err != nil {
 		return err
 	}
@@ -158,7 +164,6 @@ func post(path string, method string, apiKey string, body interface{}, data inte
 	if err != nil {
 		return err
 	}
-	defer resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return fmt.Errorf("non 200 status code returned: %d", resp.StatusCode)
@@ -170,6 +175,7 @@ func post(path string, method string, apiKey string, body interface{}, data inte
 			return err
 		}
 	}
+	_ = resp.Body.Close()
 
 	return nil
 }

+ 1 - 0
internal/billing/stripe.go

@@ -20,6 +20,7 @@ type StripeClient struct {
 	PublishableKey string
 }
 
+// NewStripeClient creates a new client to call the Stripe API
 func NewStripeClient(secretKey string, publishableKey string) *StripeClient {
 	return &StripeClient{
 		SecretKey:      secretKey,

+ 3 - 1
internal/models/project.go

@@ -27,7 +27,9 @@ const (
 	CapiProvisionerEnabled FeatureFlagLabel = "capi_provisioner_enabled"
 
 	// BillingEnabled enables the "Billing" tab and all Stripe integrations
-	BillingEnabled   FeatureFlagLabel = "billing_enabled"
+	BillingEnabled FeatureFlagLabel = "billing_enabled"
+
+	// MetronomeEnabled enables all Metronome business logic
 	MetronomeEnabled FeatureFlagLabel = "metronome_enabled"
 
 	// DBEnabled enables the "Databases" tab