Jelajahi Sumber

Finish lago migration

Mauricio Araujo 2 tahun lalu
induk
melakukan
511cba3681

+ 2 - 3
api/server/handlers/billing/create.go

@@ -150,10 +150,9 @@ func (c *CreateBillingHandler) grantRewardIfReferral(ctx context.Context, referr
 		// Metronome requires an expiration to be passed in, so we set it to 5 years which in
 		// practice will mean the credits will most likely run out before expiring
 		expiresAt := time.Now().AddDate(5, 0, 0).Format(time.RFC3339)
-		reason := "Referral reward"
+		name := "Referral reward"
 		rewardAmount := c.Config().BillingManager.LagoClient.DefaultRewardAmountCents
-		paidAmount := c.Config().BillingManager.LagoClient.DefaultPaidAmountCents
-		err := c.Config().BillingManager.LagoClient.CreateCreditsGrant(ctx, referrerProject.UsageID, reason, rewardAmount, paidAmount, expiresAt)
+		err := c.Config().BillingManager.LagoClient.CreateCreditsGrant(ctx, referrerProject.ID, name, rewardAmount, expiresAt, referrerProject.EnableSandbox)
 		if err != nil {
 			return telemetry.Error(ctx, span, err, "failed to grand credits reward")
 		}

+ 1 - 2
api/server/handlers/billing/ingest.go

@@ -52,7 +52,6 @@ func (c *IngestEventsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 
 	telemetry.WithAttributes(span,
 		telemetry.AttributeKV{Key: "metronome-enabled", Value: true},
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
 	)
 
 	ingestEventsRequest := struct {
@@ -76,7 +75,7 @@ func (c *IngestEventsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		}
 	}
 
-	err := c.Config().BillingManager.LagoClient.IngestEvents(ctx, ingestEventsRequest.Events)
+	err := c.Config().BillingManager.LagoClient.IngestEvents(ctx, ingestEventsRequest.Events, proj.EnableSandbox)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error ingesting events")
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 10 - 9
api/server/handlers/billing/list.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"net/http"
 
-	"github.com/google/uuid"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
@@ -100,10 +99,9 @@ func (c *CheckPaymentEnabledHandler) ensureBillingSetup(ctx context.Context, pro
 
 	telemetry.WithAttributes(span,
 		telemetry.AttributeKV{Key: "billing-id", Value: proj.BillingID},
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
 	)
 
-	if proj.BillingID == "" || proj.UsageID == uuid.Nil {
+	if proj.BillingID == "" {
 		adminUser, err := c.getAdminUser(ctx, proj.ID)
 		if err != nil {
 			return telemetry.Error(ctx, span, err, "error getting admin user")
@@ -119,6 +117,14 @@ func (c *CheckPaymentEnabledHandler) ensureBillingSetup(ctx context.Context, pro
 		if err != nil {
 			return telemetry.Error(ctx, span, err, "error ensuring Stripe customer exists")
 		}
+	}
+
+	lagoCustomerExists := false
+	if !lagoCustomerExists {
+		adminUser, err := c.getAdminUser(ctx, proj.ID)
+		if err != nil {
+			return telemetry.Error(ctx, span, err, "error getting admin user")
+		}
 
 		// Create usage customer for project and set the usage ID if it doesn't exist
 		err = c.ensureMetronomeCustomerExists(ctx, adminUser.Email, proj)
@@ -193,7 +199,7 @@ func (c *CheckPaymentEnabledHandler) ensureMetronomeCustomerExists(ctx context.C
 	ctx, span := telemetry.NewSpan(ctx, "ensure-metronome-customer-exists")
 	defer span.End()
 
-	if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) || proj.UsageID != uuid.Nil {
+	if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) {
 		return nil
 	}
 
@@ -202,10 +208,5 @@ func (c *CheckPaymentEnabledHandler) ensureMetronomeCustomerExists(ctx context.C
 		return telemetry.Error(ctx, span, err, "error creating Metronome customer")
 	}
 
-	telemetry.WithAttributes(span,
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
-		telemetry.AttributeKV{Key: "usage-plan-id", Value: proj.UsagePlanID},
-	)
-
 	return nil
 }

+ 9 - 62
api/server/handlers/billing/plan.go

@@ -45,10 +45,9 @@ func (c *ListPlansHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 	telemetry.WithAttributes(span,
 		telemetry.AttributeKV{Key: "metronome-enabled", Value: true},
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
 	)
 
-	plan, err := c.Config().BillingManager.LagoClient.ListCustomerPlan(ctx, proj.UsageID)
+	plan, err := c.Config().BillingManager.LagoClient.ListCustomerPlan(ctx, proj.ID, proj.EnableSandbox)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error listing plans")
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
@@ -89,19 +88,18 @@ func (c *ListCreditsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	credits, err := c.Config().BillingManager.LagoClient.ListCustomerCredits(ctx, proj.UsageID)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error listing credits")
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
+	// credits, err := c.Config().BillingManager.LagoClient.ListCustomerCredits(ctx, proj.ID, proj.EnableSandbox)
+	// if err != nil {
+	// 	err := telemetry.Error(ctx, span, err, "error listing credits")
+	// 	c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+	// 	return
+	// }
 
 	telemetry.WithAttributes(span,
 		telemetry.AttributeKV{Key: "metronome-enabled", Value: true},
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
 	)
 
-	c.WriteResult(w, r, credits)
+	c.WriteResult(w, r, "")
 }
 
 // ListCustomerUsageHandler returns customer usage aggregations like CPU and RAM hours.
@@ -129,7 +127,6 @@ func (c *ListCustomerUsageHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	telemetry.WithAttributes(span,
 		telemetry.AttributeKV{Key: "metronome-config-exists", Value: c.Config().BillingManager.LagoConfigLoaded},
 		telemetry.AttributeKV{Key: "metronome-enabled", Value: proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient)},
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
 	)
 
 	if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) {
@@ -145,7 +142,7 @@ func (c *ListCustomerUsageHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	usage, err := c.Config().BillingManager.LagoClient.ListCustomerUsage(ctx, proj.UsageID, req.StartingOn, req.EndingBefore, req.WindowSize, req.CurrentPeriod)
+	usage, err := c.Config().BillingManager.LagoClient.ListCustomerUsage(ctx, proj.ID, true, proj.EnableSandbox)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error listing customer usage")
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
@@ -153,53 +150,3 @@ func (c *ListCustomerUsageHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	}
 	c.WriteResult(w, r, usage)
 }
-
-// ListCustomerCostsHandler returns customer usage aggregations like CPU and RAM hours.
-type ListCustomerCostsHandler struct {
-	handlers.PorterHandlerReadWriter
-}
-
-// NewListCustomerCostsHandler returns a new ListCustomerCostsHandler
-func NewListCustomerCostsHandler(
-	config *config.Config,
-	decoderValidator shared.RequestDecoderValidator,
-	writer shared.ResultWriter,
-) *ListCustomerCostsHandler {
-	return &ListCustomerCostsHandler{
-		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
-	}
-}
-
-func (c *ListCustomerCostsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	ctx, span := telemetry.NewSpan(r.Context(), "serve-list-customer-costs")
-	defer span.End()
-
-	proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
-
-	telemetry.WithAttributes(span,
-		telemetry.AttributeKV{Key: "metronome-config-exists", Value: c.Config().BillingManager.LagoConfigLoaded},
-		telemetry.AttributeKV{Key: "metronome-enabled", Value: proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient)},
-		telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
-	)
-
-	if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) {
-		c.WriteResult(w, r, "")
-		return
-	}
-
-	req := &types.ListCustomerCostsRequest{}
-
-	if ok := c.DecodeAndValidate(w, r, req); !ok {
-		err := telemetry.Error(ctx, span, nil, "error decoding list customer costs request")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
-	}
-
-	usage, err := c.Config().BillingManager.LagoClient.ListCustomerCosts(ctx, proj.UsageID, req.StartingOn, req.EndingBefore, req.Limit)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error listing customer costs")
-		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
-		return
-	}
-	c.WriteResult(w, r, usage)
-}

+ 2 - 9
api/server/handlers/project/create.go

@@ -3,7 +3,6 @@ package project
 import (
 	"net/http"
 
-	"github.com/google/uuid"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
@@ -102,21 +101,15 @@ func (p *ProjectCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 
 	// Create Metronome customer and add to starter plan
 	if p.Config().BillingManager.LagoConfigLoaded && proj.GetFeatureFlag(models.MetronomeEnabled, p.Config().LaunchDarklyClient) {
-		customerID, customerPlanID, err := p.Config().BillingManager.LagoClient.CreateCustomerWithPlan(ctx, user.Email, proj.Name, proj.ID, proj.BillingID, proj.EnableSandbox)
+		err := p.Config().BillingManager.LagoClient.CreateCustomerWithPlan(ctx, user.Email, proj.Name, proj.ID, proj.BillingID, proj.EnableSandbox)
 		if err != nil {
 			err = telemetry.Error(ctx, span, err, "error creating Metronome customer")
 			p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
 			return
 		}
-		proj.UsageID = customerID
-		proj.UsagePlanID = customerPlanID
-		telemetry.WithAttributes(span,
-			telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
-			telemetry.AttributeKV{Key: "usage-plan-id", Value: proj.UsagePlanID},
-		)
 	}
 
-	if proj.BillingID != "" || proj.UsageID != uuid.Nil {
+	if proj.BillingID != "" {
 		_, err = p.Repo().Project().UpdateProject(proj)
 		if err != nil {
 			err := telemetry.Error(ctx, span, err, "error updating project")

+ 1 - 6
api/server/handlers/project/delete.go

@@ -93,18 +93,13 @@ func (p *ProjectDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	}
 
 	if p.Config().BillingManager.LagoConfigLoaded && proj.GetFeatureFlag(models.MetronomeEnabled, p.Config().LaunchDarklyClient) {
-		err = p.Config().BillingManager.LagoClient.EndCustomerPlan(ctx, proj.UsageID, proj.UsagePlanID)
+		err = p.Config().BillingManager.LagoClient.EndCustomerPlan(ctx, proj.ID)
 		if err != nil {
 			e := "error ending billing plan"
 			err = telemetry.Error(ctx, span, err, e)
 			p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 			return
 		}
-		telemetry.WithAttributes(span,
-			telemetry.AttributeKV{Key: "project-id", Value: proj.ID},
-			telemetry.AttributeKV{Key: "usage-id", Value: proj.UsageID},
-			telemetry.AttributeKV{Key: "usage-plan-id", Value: proj.UsagePlanID},
-		)
 	}
 
 	deletedProject, err := p.Repo().Project().DeleteProject(proj)

+ 1 - 3
api/server/handlers/project/referrals.go

@@ -3,7 +3,6 @@ package project
 import (
 	"net/http"
 
-	"github.com/google/uuid"
 	"github.com/porter-dev/porter/api/server/handlers"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
@@ -34,8 +33,7 @@ func (c *GetProjectReferralDetailsHandler) ServeHTTP(w http.ResponseWriter, r *h
 
 	proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
 
-	if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) ||
-		proj.UsageID == uuid.Nil || !proj.EnableSandbox {
+	if !c.Config().BillingManager.LagoConfigLoaded || !proj.GetFeatureFlag(models.MetronomeEnabled, c.Config().LaunchDarklyClient) || !proj.EnableSandbox {
 		c.WriteResult(w, r, "")
 
 		telemetry.WithAttributes(span,

+ 0 - 28
api/server/router/project.go

@@ -454,34 +454,6 @@ func getProjectRoutes(
 		Router:   r,
 	})
 
-	// GET /api/projects/{project_id}/billing/costs -> project.NewListCustomerCostsHandler
-	listCustomerCostsEndpoint := factory.NewAPIEndpoint(
-		&types.APIRequestMetadata{
-			Verb:   types.APIVerbGet,
-			Method: types.HTTPVerbGet,
-			Path: &types.Path{
-				Parent:       basePath,
-				RelativePath: relPath + "/billing/costs",
-			},
-			Scopes: []types.PermissionScope{
-				types.UserScope,
-				types.ProjectScope,
-			},
-		},
-	)
-
-	listCustomerCostsHandler := billing.NewListCustomerCostsHandler(
-		config,
-		factory.GetDecoderValidator(),
-		factory.GetResultWriter(),
-	)
-
-	routes = append(routes, &router.Route{
-		Endpoint: listCustomerCostsEndpoint,
-		Handler:  listCustomerCostsHandler,
-		Router:   r,
-	})
-
 	// GET /api/projects/{project_id}/billing/invoices -> project.NewListCustomerInvoicesHandler
 	listCustomerInvoicesEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 80 - 182
internal/billing/usage.go

@@ -3,28 +3,33 @@ package billing
 import (
 	"context"
 	"fmt"
-	"net/http"
+	"strconv"
 	"time"
 
 	"github.com/getlago/lago-go-client"
-	"github.com/google/uuid"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/telemetry"
 )
 
 const (
-	defaultMaxRetries        = 10
-	porterStandardTrialDays  = 15
 	defaultRewardAmountCents = 1000
-	defaultPaidAmountCents   = 0
 	maxReferralRewards       = 10
+	defaultMaxRetries        = 10
 	maxIngestEventLimit      = 100
+
+	// These prefixes are used to build the customer and subscription IDs
+	// in Lago. This way we can reuse the project IDs instead of storing
+	// the Lago IDs in the database.
+
+	// SubscriptionIDPrefix is the prefix for the subscription ID
+	SubscriptionIDPrefix = "sub"
+	// CustomerIDPrefix is the prefix for the customer ID
+	CustomerIDPrefix = "cus"
 )
 
 // LagoClient is the client used to call the Lago API
 type LagoClient struct {
 	client               lago.Client
-	billableMetrics      []types.BillableMetric
 	PorterCloudPlanID    string
 	PorterStandardPlanID string
 
@@ -64,7 +69,7 @@ func (m LagoClient) CreateCustomerWithPlan(ctx context.Context, userEmail string
 		return telemetry.Error(ctx, span, err, fmt.Sprintf("error while creating customer with plan %s", planID))
 	}
 
-	subscriptionID := m.generateSubscriptionID(projectID, sandboxEnabled)
+	subscriptionID := m.GenerateLagoID(SubscriptionIDPrefix, projectID, sandboxEnabled)
 
 	err = m.addCustomerPlan(ctx, customerID, planID, subscriptionID)
 
@@ -76,7 +81,7 @@ func (m LagoClient) createCustomer(ctx context.Context, userEmail string, projec
 	ctx, span := telemetry.NewSpan(ctx, "create-metronome-customer")
 	defer span.End()
 
-	customerID = m.generateCustomerID(projectID, sandboxEnabled)
+	customerID = m.GenerateLagoID(CustomerIDPrefix, projectID, sandboxEnabled)
 
 	customerInput := &lago.CustomerInput{
 		ExternalID: customerID,
@@ -122,14 +127,15 @@ func (m LagoClient) addCustomerPlan(ctx context.Context, projectID string, planI
 }
 
 // ListCustomerPlan will return the current active plan to which the user is subscribed
-func (m LagoClient) ListCustomerPlan(ctx context.Context, subscriptionID string) (plan types.Plan, err error) {
+func (m LagoClient) ListCustomerPlan(ctx context.Context, projectID uint, sandboxEnabled bool) (plan types.Plan, err error) {
 	ctx, span := telemetry.NewSpan(ctx, "list-customer-plans")
 	defer span.End()
 
-	if subscriptionID == "" {
-		return plan, telemetry.Error(ctx, span, err, "subscription id empty")
+	if projectID == 0 {
+		return plan, telemetry.Error(ctx, span, err, "project id empty")
 	}
 
+	subscriptionID := m.GenerateLagoID(SubscriptionIDPrefix, projectID, sandboxEnabled)
 	subscription, lagoErr := m.client.Subscription().Get(ctx, subscriptionID)
 	if err != nil {
 		return plan, telemetry.Error(ctx, span, lagoErr.Err, "failed to create subscription")
@@ -143,14 +149,15 @@ func (m LagoClient) ListCustomerPlan(ctx context.Context, subscriptionID string)
 }
 
 // EndCustomerPlan will immediately end the plan for the given customer
-func (m LagoClient) EndCustomerPlan(ctx context.Context, subscriptionID string) (err error) {
+func (m LagoClient) EndCustomerPlan(ctx context.Context, projectID uint) (err error) {
 	ctx, span := telemetry.NewSpan(ctx, "end-metronome-customer-plan")
 	defer span.End()
 
-	if subscriptionID == "" {
+	if projectID == 0 {
 		return telemetry.Error(ctx, span, err, "subscription id empty")
 	}
 
+	subscriptionID := m.GenerateLagoID(SubscriptionIDPrefix, projectID, false)
 	subscriptionTerminateInput := lago.SubscriptionTerminateInput{
 		ExternalID: subscriptionID,
 	}
@@ -164,41 +171,42 @@ func (m LagoClient) EndCustomerPlan(ctx context.Context, subscriptionID string)
 }
 
 // ListCustomerCredits will return the total number of credits for the customer
-func (m LagoClient) ListCustomerCredits(ctx context.Context, customerID string) (credits types.ListCreditGrantsResponse, err error) {
-	ctx, span := telemetry.NewSpan(ctx, "list-customer-credits")
-	defer span.End()
+// func (m LagoClient) ListCustomerCredits(ctx context.Context, customerID string) (credits types.ListCreditGrantsResponse, err error) {
+// 	ctx, span := telemetry.NewSpan(ctx, "list-customer-credits")
+// 	defer span.End()
 
-	if customerID == "" {
-		return credits, telemetry.Error(ctx, span, err, "customer id empty")
-	}
+// 	if customerID == "" {
+// 		return credits, telemetry.Error(ctx, span, err, "customer id empty")
+// 	}
 
-	walletListInput := &lago.WalletListInput{
-		ExternalCustomerID: customerID,
-	}
+// 	walletListInput := &lago.WalletListInput{
+// 		ExternalCustomerID: customerID,
+// 	}
 
-	walletList, lagoErr := m.client.Wallet().GetList(ctx, walletListInput)
-	if lagoErr.Err != nil {
-		return credits, telemetry.Error(ctx, span, lagoErr.Err, "failed to get wallet")
-	}
+// 	walletList, lagoErr := m.client.Wallet().GetList(ctx, walletListInput)
+// 	if lagoErr.Err != nil {
+// 		return credits, telemetry.Error(ctx, span, lagoErr.Err, "failed to get wallet")
+// 	}
 
-	var response types.ListCreditGrantsResponse
-	for _, wallet := range walletList.Wallets {
-		response.GrantedBalanceCents += wallet.BalanceCents
-		response.RemainingBalanceCents += wallet.OngoingUsageBalanceCents
-	}
+// 	var response types.ListCreditGrantsResponse
+// 	for _, wallet := range walletList.Wallets {
+// 		response.GrantedBalanceCents += wallet.BalanceCents
+// 		response.RemainingBalanceCents += wallet.OngoingUsageBalanceCents
+// 	}
 
-	return response, nil
-}
+// 	return response, nil
+// }
 
 // CreateCreditsGrant will create a new credit grant for the customer with the specified amount
-func (m LagoClient) CreateCreditsGrant(ctx context.Context, customerID string, reason string, grantAmount float64, expiresAt string) (err error) {
+func (m LagoClient) CreateCreditsGrant(ctx context.Context, projectID uint, name string, grantAmount int64, expiresAt string, sandboxEnabled bool) (err error) {
 	ctx, span := telemetry.NewSpan(ctx, "create-credits-grant")
 	defer span.End()
 
-	if customerID == "" {
-		return telemetry.Error(ctx, span, err, "customer id empty")
+	if projectID == 0 {
+		return telemetry.Error(ctx, span, err, "project id empty")
 	}
 
+	customerID := m.GenerateLagoID(CustomerIDPrefix, projectID, sandboxEnabled)
 	expiresAtTime, err := time.Parse(time.RFC3339, expiresAt)
 	if err != nil {
 		return telemetry.Error(ctx, span, err, "failed to parse credit expiration timestamp")
@@ -206,8 +214,10 @@ func (m LagoClient) CreateCreditsGrant(ctx context.Context, customerID string, r
 
 	walletInput := &lago.WalletInput{
 		ExternalCustomerID: customerID,
+		Name:               name,
 		Currency:           lago.USD,
-		RateAmount:         fmt.Sprintf("%.2f", grantAmount),
+		GrantedCredits:     strconv.FormatInt(grantAmount, 10),
+		RateAmount:         "1",
 		ExpirationAt:       &expiresAtTime,
 	}
 
@@ -220,110 +230,30 @@ func (m LagoClient) CreateCreditsGrant(ctx context.Context, customerID string, r
 }
 
 // ListCustomerUsage will return the aggregated usage for a customer
-func (m LagoClient) ListCustomerUsage(ctx context.Context, customerID uuid.UUID, startingOn string, endingBefore string, windowsSize string, currentPeriod bool) (usage []types.Usage, err error) {
+func (m LagoClient) ListCustomerUsage(ctx context.Context, projectID uint, currentPeriod bool, sandboxEnabled bool) (usage []types.Usage, err error) {
 	ctx, span := telemetry.NewSpan(ctx, "list-customer-usage")
 	defer span.End()
 
-	if customerID == uuid.Nil {
-		return usage, telemetry.Error(ctx, span, err, "customer id empty")
-	}
-
-	if len(m.billableMetrics) == 0 {
-		billableMetrics, err := m.listBillableMetricIDs(ctx, customerID)
-		if err != nil {
-			return nil, telemetry.Error(ctx, span, err, "failed to list billable metrics")
-		}
-
-		telemetry.WithAttributes(span,
-			telemetry.AttributeKV{Key: "billable-metric-count", Value: len(billableMetrics)},
-		)
-
-		// Cache billable metric ids for future calls
-		m.billableMetrics = append(m.billableMetrics, billableMetrics...)
-	}
-
-	path := "usage/groups"
-
-	startingOnTimestamp, endingBeforeTimestamp, err := parseAndCheckTimestamps(startingOn, endingBefore)
-	if err != nil {
-		return nil, telemetry.Error(ctx, span, err, err.Error())
+	if projectID == 0 {
+		return usage, telemetry.Error(ctx, span, err, "project id empty")
 	}
 
-	baseReq := types.ListCustomerUsageRequest{
-		CustomerID:    customerID,
-		WindowSize:    windowsSize,
-		StartingOn:    startingOnTimestamp,
-		EndingBefore:  endingBeforeTimestamp,
-		CurrentPeriod: currentPeriod,
+	subscriptionID := m.GenerateLagoID(SubscriptionIDPrefix, projectID, sandboxEnabled)
+	customerUsageInput := &lago.CustomerUsageInput{
+		ExternalSubscriptionID: subscriptionID,
 	}
 
-	for _, billableMetric := range m.billableMetrics {
-		telemetry.WithAttributes(span,
-			telemetry.AttributeKV{Key: "billable-metric-id", Value: billableMetric.ID},
-		)
-
-		var result struct {
-			Data []types.CustomerUsageMetric `json:"data"`
-		}
-
-		baseReq.BillableMetricID = billableMetric.ID
-		_, err = m.do(http.MethodPost, path, "", baseReq, &result)
-		if err != nil {
-			return usage, telemetry.Error(ctx, span, err, "failed to get customer usage")
-		}
-
-		usage = append(usage, types.Usage{
-			MetricName:   billableMetric.Name,
-			UsageMetrics: result.Data,
-		})
+	customerID := m.GenerateLagoID(CustomerIDPrefix, projectID, sandboxEnabled)
+	_, lagoErr := m.client.Customer().CurrentUsage(ctx, customerID, customerUsageInput)
+	if lagoErr.Err != nil {
+		return usage, telemetry.Error(ctx, span, lagoErr.Err, "failed to get customer usage")
 	}
 
 	return usage, nil
 }
 
-// ListCustomerCosts will return the costs for a customer over a time period
-func (m LagoClient) ListCustomerCosts(ctx context.Context, customerID uuid.UUID, startingOn string, endingBefore string, limit int) (costs []types.FormattedCost, err error) {
-	ctx, span := telemetry.NewSpan(ctx, "list-customer-costs")
-	defer span.End()
-
-	if customerID == uuid.Nil {
-		return costs, telemetry.Error(ctx, span, err, "customer id empty")
-	}
-
-	path := fmt.Sprintf("customers/%s/costs", customerID)
-
-	var result struct {
-		Data []types.Cost `json:"data"`
-	}
-
-	startingOnTimestamp, endingBeforeTimestamp, err := parseAndCheckTimestamps(startingOn, endingBefore)
-	if err != nil {
-		return nil, telemetry.Error(ctx, span, err, err.Error())
-	}
-
-	queryParams := fmt.Sprintf("starting_on=%s&ending_before=%s&limit=%d", startingOnTimestamp, endingBeforeTimestamp, limit)
-
-	_, err = m.do(http.MethodGet, path, queryParams, nil, &result)
-	if err != nil {
-		return costs, telemetry.Error(ctx, span, err, "failed to create credits grant")
-	}
-
-	for _, customerCost := range result.Data {
-		formattedCost := types.FormattedCost{
-			StartTimestamp: customerCost.StartTimestamp,
-			EndTimestamp:   customerCost.EndTimestamp,
-		}
-		for _, creditType := range customerCost.CreditTypes {
-			formattedCost.Cost += creditType.Cost
-		}
-		costs = append(costs, formattedCost)
-	}
-
-	return costs, nil
-}
-
 // IngestEvents sends a list of billing events to Metronome's ingest endpoint
-func (m LagoClient) IngestEvents(ctx context.Context, events []types.BillingEvent) (err error) {
+func (m LagoClient) IngestEvents(ctx context.Context, events []types.BillingEvent, enableSandbox bool) (err error) {
 	ctx, span := telemetry.NewSpan(ctx, "ingets-billing-events")
 	defer span.End()
 
@@ -331,8 +261,6 @@ func (m LagoClient) IngestEvents(ctx context.Context, events []types.BillingEven
 		return nil
 	}
 
-	path := "ingest"
-
 	for i := 0; i < len(events); i += maxIngestEventLimit {
 		end := i + maxIngestEventLimit
 		if end > len(events) {
@@ -340,29 +268,29 @@ func (m LagoClient) IngestEvents(ctx context.Context, events []types.BillingEven
 		}
 
 		batch := events[i:end]
+		batchInput := make([]lago.EventInput, len(batch))
 
-		// Retry each batch to make sure all events are ingested
-		var currentAttempts int
-		for currentAttempts < defaultMaxRetries {
-			statusCode, err := m.do(http.MethodPost, path, "", batch, nil)
-			// Check errors that are not from error http codes
-			if statusCode == 0 && err != nil {
-				return telemetry.Error(ctx, span, err, "failed to ingest billing events")
+		for i := range batch {
+			customerID, err := strconv.ParseUint(batch[i].CustomerID, 10, 64)
+			if err != nil {
+				return telemetry.Error(ctx, span, err, "failed to parse customer ID")
 			}
-
-			if statusCode == http.StatusForbidden || statusCode == http.StatusUnauthorized {
-				return telemetry.Error(ctx, span, err, "unauthorized")
-			}
-
-			// 400 responses should not be retried
-			if statusCode == http.StatusBadRequest {
-				return telemetry.Error(ctx, span, err, "malformed billing events")
+			externalSubscriptionID := m.GenerateLagoID(SubscriptionIDPrefix, uint(customerID), enableSandbox)
+
+			event := lago.EventInput{
+				TransactionID:          batch[i].TransactionID,
+				ExternalSubscriptionID: externalSubscriptionID,
+				Code:                   batch[i].EventType,
+				Timestamp:              batch[i].Timestamp,
+				Properties:             batch[i].Properties,
 			}
+			batchInput = append(batchInput, event)
+		}
 
-			// Any other status code can be safely retried
-			if statusCode == http.StatusOK {
-				break
-			}
+		// Retry each batch to make sure all events are ingested
+		var currentAttempts int
+		for currentAttempts < defaultMaxRetries {
+			m.client.Event().Batch(ctx, &batchInput)
 			currentAttempts++
 		}
 
@@ -374,40 +302,10 @@ func (m LagoClient) IngestEvents(ctx context.Context, events []types.BillingEven
 	return nil
 }
 
-func (m LagoClient) listBillableMetricIDs(ctx context.Context, customerID uuid.UUID) (billableMetrics []types.BillableMetric, err error) {
-	ctx, span := telemetry.NewSpan(ctx, "list-billable-metrics")
-	defer span.End()
-
-	if customerID == uuid.Nil {
-		return billableMetrics, telemetry.Error(ctx, span, err, "customer id empty")
-	}
-
-	path := fmt.Sprintf("/customers/%s/billable-metrics", customerID)
-
-	var result struct {
-		Data []types.BillableMetric `json:"data"`
-	}
-
-	_, err = m.do(http.MethodGet, path, "", nil, &result)
-	if err != nil {
-		return billableMetrics, telemetry.Error(ctx, span, err, "failed to retrieve billable metrics from metronome")
-	}
-
-	return result.Data, nil
-}
-
-func (m LagoClient) generateCustomerID(projectID uint, sandboxEnabled bool) string {
-	if sandboxEnabled {
-		return fmt.Sprintf("cloud_cus_%d", projectID)
-	}
-
-	return fmt.Sprintf("cus_%d", projectID)
-}
-
-func (m LagoClient) generateSubscriptionID(projectID uint, sandboxEnabled bool) string {
+func (m LagoClient) GenerateLagoID(prefix string, projectID uint, sandboxEnabled bool) string {
 	if sandboxEnabled {
-		return fmt.Sprintf("cloud_sub_%d", projectID)
+		return fmt.Sprintf("cloud_%s_%d", prefix, projectID)
 	}
 
-	return fmt.Sprintf("sub_%d", projectID)
+	return fmt.Sprintf("%s_%d", prefix, projectID)
 }