Pārlūkot izejas kodu

Add list credits api call, remove unnecessary types

Mauricio Araujo 2 gadi atpakaļ
vecāks
revīzija
f521e90110

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

@@ -88,14 +88,14 @@ func (c *ListCreditsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		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
-	// }
-
-	c.WriteResult(w, r, "")
+	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
+	}
+
+	c.WriteResult(w, r, credits)
 }
 
 // ListCustomerUsageHandler returns customer usage aggregations like CPU and RAM hours.
@@ -138,7 +138,18 @@ func (c *ListCustomerUsageHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 		return
 	}
 
-	usage, err := c.Config().BillingManager.LagoClient.ListCustomerUsage(ctx, proj.ID, true, proj.EnableSandbox)
+	plan, err := c.Config().BillingManager.LagoClient.GetCustomeActivePlan(ctx, proj.ID, proj.EnableSandbox)
+	if err != nil {
+		err := telemetry.Error(ctx, span, err, "error getting active subscription")
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	telemetry.WithAttributes(span,
+		telemetry.AttributeKV{Key: "subscription_id", Value: plan.ID},
+	)
+
+	usage, err := c.Config().BillingManager.LagoClient.ListCustomerUsage(ctx, plan.CustomerID, plan.ID, req.CurrentPeriod)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error listing customer usage")
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 23 - 186
api/types/billing_usage.go

@@ -1,85 +1,5 @@
 package types
 
-import "github.com/google/uuid"
-
-// Customer represents a customer in Metronome
-type Customer struct {
-	ID   uuid.UUID `json:"id"`
-	Name string    `json:"name"`
-	// Aliases are alternative ids that can be used to refer to this customer in usage events
-	Aliases       []string          `json:"ingest_aliases"`
-	BillingConfig BillingConfig     `json:"billing_config,omitempty"`
-	CustomFields  map[string]string `json:"custom_fields,omitempty"`
-}
-
-// CustomerArchiveRequest will archive the customer in Metronome.
-type CustomerArchiveRequest struct {
-	CustomerID uuid.UUID `json:"id"`
-}
-
-// BillingConfig is the configuration for the billing provider (Stripe, etc.)
-type BillingConfig struct {
-	// BillingProviderType is the name of the billing provider (e.g. )
-	BillingProviderType       string `json:"billing_provider_type"`
-	BillingProviderCustomerID string `json:"billing_provider_customer_id"`
-	// StripeCollectionMethod defines if invoices are charged automatically or sent to customers
-	StripeCollectionMethod string `json:"stripe_collection_method"`
-}
-
-// AddCustomerPlanRequest represents a request to add a customer plan with specific details.
-type AddCustomerPlanRequest struct {
-	PlanID uuid.UUID `json:"plan_id"`
-	// StartingOn is a RFC3339 timestamp for when the plan becomes active for this customer. Must be at 0:00 UTC (midnight)
-	StartingOnUTC string `json:"starting_on"`
-	// EndingBeforeUTC is a RFC 3339 timestamp for when the plan ends (exclusive) for this customer. Must be at 0:00 UTC (midnight)
-	EndingBeforeUTC string `json:"ending_before,omitempty"`
-	// NetPaymentTermDays is the number of days after issuance of invoice after which the invoice is due
-	NetPaymentTermDays int `json:"net_payment_terms_days,omitempty"`
-	// Trial is the trial period for the plan
-	Trial *TrialSpec `json:"trial_spec,omitempty"`
-}
-
-// TrialSpec is the trial period for the plan
-type TrialSpec struct {
-	LengthInDays int64 `json:"length_in_days"`
-}
-
-// EndCustomerPlanRequest represents a request to end the plan for a specific customer.
-type EndCustomerPlanRequest struct {
-	// EndingBeforeUTC is a RFC 3339 timestamp for when the plan ends (exclusive) for this customer. Must be at 0:00 UTC (midnight).
-	EndingBeforeUTC string `json:"ending_before,omitempty"`
-	// VoidInvoices determines if Metronome invoices are voided. If set to true, the plan end date can be before the last finalized invoice date.
-	// and any invoices generated after the plan end date will be voided.
-	VoidInvoices bool `json:"void_invoices"`
-	// VoidStripeInvoices determines if Stripe invoices are void (if VoidInvoices is set to true). Drafts will be deleted.
-	VoidStripeInvoices bool `json:"void_stripe_invoices"`
-}
-
-// CreateCreditsGrantRequest is the request to create a credit grant for a customer
-type CreateCreditsGrantRequest struct {
-	// CustomerID is the id of the customer
-	CustomerID    uuid.UUID     `json:"customer_id"`
-	UniquenessKey string        `json:"uniqueness_key"`
-	GrantAmount   GrantAmountID `json:"grant_amount"`
-	PaidAmount    PaidAmount    `json:"paid_amount"`
-	Name          string        `json:"name"`
-	ExpiresAt     string        `json:"expires_at"`
-	Priority      int           `json:"priority"`
-	Reason        string        `json:"reason"`
-}
-
-// ListCreditGrantsRequest is the request to list a user's credit grants. Note that only one of
-// CreditTypeIDs, CustomerIDs, or CreditGrantIDs must be specified.
-type ListCreditGrantsRequest struct {
-	CreditTypeIDs  []uuid.UUID `json:"credit_type_ids,omitempty"`
-	CustomerIDs    []uuid.UUID `json:"customer_ids,omitempty"`
-	CreditGrantIDs []uuid.UUID `json:"credit_grant_ids,omitempty"`
-	// NotExpiringBefore will return grants that expire at or after this RFC 3339 timestamp.
-	NotExpiringBefore string `json:"not_expiring_before,omitempty"`
-	// EffectiveBefore will return grants that are effective before this RFC 3339 timestamp (exclusive).
-	EffectiveBefore string `json:"effective_before,omitempty"`
-}
-
 // ListCreditGrantsResponse returns the total remaining and granted credits for a customer.
 type ListCreditGrantsResponse struct {
 	RemainingBalanceCents int `json:"remaining_credits"`
@@ -88,70 +8,34 @@ type ListCreditGrantsResponse struct {
 
 // ListCustomerUsageRequest is the request to list usage for a customer
 type ListCustomerUsageRequest struct {
-	CustomerID       uuid.UUID `json:"customer_id"`
-	BillableMetricID uuid.UUID `json:"billable_metric_id"`
-	WindowSize       string    `json:"window_size"`
-	StartingOn       string    `json:"starting_on,omitempty"`
-	EndingBefore     string    `json:"ending_before,omitempty"`
-	CurrentPeriod    bool      `json:"current_period,omitempty"`
+	CurrentPeriod bool `json:"current_period,omitempty"`
 }
 
 // Usage is the aggregated usage for a customer
 type Usage struct {
-	MetricName   string                `json:"metric_name"`
-	UsageMetrics []CustomerUsageMetric `json:"usage_metrics"`
+	FromDatetime     string        `json:"from_datetime"`
+	ToDatetime       string        `json:"to_datetime"`
+	TotalAmountCents int64         `json:"total_amount_cents"`
+	ChargesUsage     []ChargeUsage `json:"charges_usage"`
 }
 
-// CustomerUsageMetric is a metric representing usage for a customer
-type CustomerUsageMetric struct {
-	StartingOn   string  `json:"starting_on"`
-	EndingBefore string  `json:"ending_before"`
-	Value        float64 `json:"value"`
+// ChargeUsage is the usage for a charge
+type ChargeUsage struct {
+	Units          string         `json:"units"`
+	AmountCents    int64          `json:"amount_cents"`
+	AmountCurrency string         `json:"amount_currency"`
+	BillableMetric BillableMetric `json:"billable_metric"`
 }
 
-// BillableMetric is defined in Metronome and represents the events that will
-// be ingested
+// BillableMetric is the metric collected for billing
 type BillableMetric struct {
-	ID   uuid.UUID `json:"id"`
-	Name string    `json:"name"`
-}
-
-// ListCustomerCostsRequest is the request to list costs for a customer
-type ListCustomerCostsRequest struct {
-	StartingOn   string `schema:"starting_on"`
-	EndingBefore string `schema:"ending_before"`
-	Limit        int    `schema:"limit"`
-}
-
-// Cost is the cost for a customer in a specific time range
-type Cost struct {
-	StartTimestamp string                    `json:"start_timestamp"`
-	EndTimestamp   string                    `json:"end_timestamp"`
-	CreditTypes    map[string]CreditTypeCost `json:"credit_types"`
-}
-
-// CreditTypeCost is the cost for a specific credit type (e.g. CPU hours)
-type CreditTypeCost struct {
-	Name              string                  `json:"name"`
-	Cost              float64                 `json:"cost"`
-	LineItemBreakdown []LineItemBreakdownCost `json:"line_item_breakdown"`
-}
-
-// LineItemBreakdownCost is the cost breakdown by line item
-type LineItemBreakdownCost struct {
-	Name string  `json:"name"`
-	Cost float64 `json:"cost"`
-}
-
-// FormattedCost is the cost for a customer in a specific time range, flattened from the Metronome response
-type FormattedCost struct {
-	StartTimestamp string  `json:"start_timestamp"`
-	EndTimestamp   string  `json:"end_timestamp"`
-	Cost           float64 `json:"cost"`
+	Name string `json:"name"`
 }
 
+// Plan is the plan for a customer
 type Plan struct {
 	ID           string `json:"id"`
+	CustomerID   string `json:"customer_id"`
 	StartingOn   string `json:"starting_on"`
 	EndingBefore string `json:"ending_before"`
 	TrialInfo    Trial  `json:"trial_info,omitempty"`
@@ -162,61 +46,6 @@ type Trial struct {
 	EndingBefore string `json:"ending_before"`
 }
 
-// CreditType is the type of the credit used in the credit grant
-type CreditType struct {
-	Name string `json:"name"`
-	ID   string `json:"id"`
-}
-
-// GrantAmountID represents the amount of credits granted with the credit type ID
-// for the create credits grant request
-type GrantAmountID struct {
-	Amount       float64   `json:"amount"`
-	CreditTypeID uuid.UUID `json:"credit_type_id"`
-}
-
-// GrantAmount represents the amount of credits granted with the credit type
-// for the list credit grants response
-type GrantAmount struct {
-	Amount     float64    `json:"amount"`
-	CreditType CreditType `json:"credit_type"`
-}
-
-// PaidAmount represents the amount paid by the customer
-type PaidAmount struct {
-	Amount       float64   `json:"amount"`
-	CreditTypeID uuid.UUID `json:"credit_type_id"`
-}
-
-// PricingUnit represents the unit of the pricing (e.g. USD, MXN, CPU hours)
-type PricingUnit struct {
-	ID         uuid.UUID `json:"id"`
-	Name       string    `json:"name"`
-	IsCurrency bool      `json:"is_currency"`
-}
-
-// Balance represents the effective balance of the grant as of the end of the customer's
-// current billing period.
-type Balance struct {
-	// ExcludingPending is the grant's current balance excluding pending deductions
-	ExcludingPending float64 `json:"excluding_pending"`
-	// IncludingPending is the grant's current balance including pending deductions
-	IncludingPending float64 `json:"including_pending"`
-	// EffectiveAt is a RFC3339 timestamp that can be used to filter credit grants by effective date
-	EffectiveAt string `json:"effective_at"`
-}
-
-// CreditGrant is a grant given to a specific user on a specific plan
-type CreditGrant struct {
-	ID          uuid.UUID   `json:"id"`
-	Name        string      `json:"name"`
-	GrantAmount GrantAmount `json:"grant_amount"`
-	Balance     Balance     `json:"balance"`
-	Reason      string      `json:"reason"`
-	EffectiveAt string      `json:"effective_at"`
-	ExpiresAt   string      `json:"expires_at"`
-}
-
 // BillingEvent represents a Metronome billing event.
 type BillingEvent struct {
 	CustomerID    string                 `json:"customer_id"`
@@ -225,3 +54,11 @@ type BillingEvent struct {
 	TransactionID string                 `json:"transaction_id"`
 	Timestamp     string                 `json:"timestamp"`
 }
+
+// Wallet represents a customer credits wallet
+type Wallet struct {
+	Status                   string `json:"status"`
+	BalanceCents             int    `json:"balance_cents,omitempty"`
+	OngoingBalanceCents      int    `json:"ongoing_balance_cents,omitempty"`
+	OngoingUsageBalanceCents int    `json:"ongoing_usage_balance_cents,omitempty"`
+}

+ 16 - 22
dashboard/src/lib/billing/types.tsx

@@ -20,29 +20,31 @@ export type Plan = z.infer<typeof PlanValidator>;
 export const PlanValidator = z
   .object({
     id: z.string(),
-    plan_name: z.string(),
-    plan_description: z.string(),
     starting_on: z.string(),
+    ending_before: z.string(),
     trial_info: TrialValidator,
   })
   .nullable();
 
-export type UsageMetric = z.infer<typeof UsageMetricValidator>;
-export const UsageMetricValidator = z.object({
-  // starting_on and ending_before are RFC 3339 date strings
-  // that represent the timeframe where the metric was ingested.
-  // If the granularity is set per day, the starting_on field
-  // represents the day the metric was ingested.
-  starting_on: z.string(),
-  ending_before: z.string(),
-  value: z.number(),
+export type BillableMetric = z.infer<typeof BillableMetricValidator>;
+export const BillableMetricValidator = z.object({
+  name: z.string(),
+});
+
+export type ChargeUsage = z.infer<typeof ChargeUsageValidator>;
+export const ChargeUsageValidator = z.object({
+  units: z.string(),
+  amount_cents: z.number(),
+  amount_currency: z.string(),
+  billable_metric: BillableMetricValidator,
 });
 
-export type UsageList = Usage[];
 export type Usage = z.infer<typeof UsageValidator>;
 export const UsageValidator = z.object({
-  metric_name: z.string(),
-  usage_metrics: z.array(UsageMetricValidator),
+  from_datetime: z.string(),
+  to_datetime: z.string(),
+  total_amount_cents: z.number(),
+  charges_usage: z.array(ChargeUsageValidator),
 });
 
 export type CreditGrants = z.infer<typeof CreditGrantsValidator>;
@@ -51,14 +53,6 @@ export const CreditGrantsValidator = z.object({
   remaining_credits: z.number(),
 });
 
-export type CostList = Cost[];
-export type Cost = z.infer<typeof CostValidator>;
-export const CostValidator = z.object({
-  start_timestamp: z.string(),
-  end_timestamp: z.string(),
-  cost: z.number(),
-});
-
 export type InvoiceList = Invoice[];
 export type Invoice = z.infer<typeof InvoiceValidator>;
 export const InvoiceValidator = z.object({

+ 6 - 61
dashboard/src/lib/hooks/useMetronome.ts → dashboard/src/lib/hooks/useLago.ts

@@ -2,18 +2,16 @@ import { useContext } from "react";
 import { useQuery } from "@tanstack/react-query";
 
 import {
-  CostValidator,
   CreditGrantsValidator,
   InvoiceValidator,
   PlanValidator,
   ReferralDetailsValidator,
   UsageValidator,
-  type CostList,
   type CreditGrants,
   type InvoiceList,
   type Plan,
   type ReferralDetails,
-  type UsageList,
+  type Usage,
 } from "lib/billing/types";
 
 import api from "shared/api";
@@ -32,11 +30,7 @@ type TGetInvoices = {
 };
 
 type TGetUsage = {
-  usage: UsageList | null;
-};
-
-type TGetCosts = {
-  costs: CostList | null;
+  usage: Usage | null;
 };
 
 type TGetReferralDetails = {
@@ -100,7 +94,6 @@ export const useCustomerPlan = (): TGetPlan => {
           {},
           { project_id: currentProject.id }
         );
-
         const plan = PlanValidator.parse(res.data);
         return plan;
       } catch (error) {
@@ -117,14 +110,14 @@ export const useCustomerPlan = (): TGetPlan => {
 export const useCustomerUsage = (
   startingOn: Date | null,
   endingBefore: Date | null,
-  windowSize: string
+  currentPeriod: boolean
 ): TGetUsage => {
   const { currentProject } = useContext(Context);
 
   // Fetch customer usage
   const usageReq = useQuery(
     ["listCustomerUsage", currentProject?.id],
-    async (): Promise<UsageList | null> => {
+    async (): Promise<Usage | null> => {
       if (!currentProject?.metronome_enabled) {
         return null;
       }
@@ -143,13 +136,13 @@ export const useCustomerUsage = (
           {
             starting_on: startingOn.toISOString(),
             ending_before: endingBefore.toISOString(),
-            window_size: windowSize,
+            current_period: currentPeriod,
           },
           {
             project_id: currentProject?.id,
           }
         );
-        const usage = UsageValidator.array().parse(res.data);
+        const usage = UsageValidator.parse(res.data);
         return usage;
       } catch (error) {
         return null;
@@ -162,54 +155,6 @@ export const useCustomerUsage = (
   };
 };
 
-export const useCustomerCosts = (
-  startingOn: Date | null,
-  endingBefore: Date | null,
-  limit: number
-): TGetCosts => {
-  const { currentProject } = useContext(Context);
-
-  // Fetch customer costs
-  const usageReq = useQuery(
-    ["listCustomerCosts", currentProject?.id],
-    async (): Promise<CostList | null> => {
-      if (!currentProject?.metronome_enabled) {
-        return null;
-      }
-
-      if (!currentProject?.id || currentProject.id === -1) {
-        return null;
-      }
-
-      if (startingOn === null || endingBefore === null) {
-        return null;
-      }
-
-      try {
-        const res = await api.getCustomerCosts(
-          "<token>",
-          {},
-          {
-            project_id: currentProject?.id,
-            starting_on: startingOn.toISOString(),
-            ending_before: endingBefore.toISOString(),
-            limit,
-          }
-        );
-
-        const costs = CostValidator.array().parse(res.data);
-        return costs;
-      } catch (error) {
-        return null;
-      }
-    }
-  );
-
-  return {
-    costs: usageReq.data ?? null,
-  };
-};
-
 export const useReferralDetails = (): TGetReferralDetails => {
   const { currentProject } = useContext(Context);
 

+ 1 - 1
dashboard/src/main/home/Home.tsx

@@ -19,7 +19,7 @@ import Link from "components/porter/Link";
 import Modal from "components/porter/Modal";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
-import { useCustomerPlan } from "lib/hooks/useMetronome";
+import { useCustomerPlan } from "lib/hooks/useLago";
 import { checkIfProjectHasPayment } from "lib/hooks/useStripe";
 
 import api from "shared/api";

+ 1 - 1
dashboard/src/main/home/app-dashboard/apps/Apps.tsx

@@ -34,7 +34,7 @@ import {
   useDeploymentTargetList,
   type DeploymentTarget,
 } from "lib/hooks/useDeploymentTarget";
-import { useCustomerPlan } from "lib/hooks/useMetronome";
+import { useCustomerPlan } from "lib/hooks/useLago";
 import { checkIfProjectHasPayment } from "lib/hooks/useStripe";
 
 import api from "shared/api";

+ 2 - 1
dashboard/src/main/home/project-settings/BillingPage.tsx

@@ -18,9 +18,10 @@ import Text from "components/porter/Text";
 import {
   useCustomerInvoices,
   useCustomerPlan,
+  useCustomerUsage,
   usePorterCredits,
   useReferralDetails,
-} from "lib/hooks/useMetronome";
+} from "lib/hooks/useLago";
 import {
   checkIfProjectHasPayment,
   usePaymentMethods,

+ 1 - 1
dashboard/src/main/home/project-settings/UsagePage.tsx

@@ -12,7 +12,7 @@ import {
   useCustomerCosts,
   useCustomerPlan,
   useCustomerUsage,
-} from "lib/hooks/useMetronome";
+} from "lib/hooks/useLago";
 
 import Bars from "./Bars";
 

+ 0 - 1
dashboard/src/shared/api.tsx

@@ -3542,7 +3542,6 @@ const getPublishableKey = baseApi<
 
 const getCustomerUsage = baseApi<
   {
-    window_size: string;
     starting_on?: string;
     ending_before?: string;
     current_period?: boolean;

+ 77 - 31
internal/billing/usage.go

@@ -2,7 +2,9 @@ package billing
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
+	"net/http"
 	"strconv"
 	"strings"
 	"time"
@@ -37,6 +39,7 @@ const (
 // LagoClient is the client used to call the Lago API
 type LagoClient struct {
 	client                 lago.Client
+	lagoApiKey             string
 	PorterCloudPlanCode    string
 	PorterStandardPlanCode string
 	PorterTrialCode        string
@@ -58,6 +61,7 @@ func NewLagoClient(lagoApiKey string, porterCloudPlanCode string, porterStandard
 	// lagoClient.Debug = true
 
 	return LagoClient{
+		lagoApiKey:               lagoApiKey,
 		client:                   *lagoClient,
 		PorterCloudPlanCode:      porterCloudPlanCode,
 		PorterStandardPlanCode:   porterStandardPlanCode,
@@ -168,6 +172,7 @@ func (m LagoClient) GetCustomeActivePlan(ctx context.Context, projectID uint, sa
 		}
 
 		plan.ID = subscription.ExternalID
+		plan.CustomerID = subscription.ExternalCustomerID
 		plan.StartingOn = subscription.SubscriptionAt.Format(time.RFC3339)
 		plan.EndingBefore = subscription.EndingAt.Format(time.RFC3339)
 
@@ -204,31 +209,55 @@ func (m LagoClient) EndCustomerPlan(ctx context.Context, projectID uint) (err er
 }
 
 // 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, projectID uint, sandboxEnabled bool) (credits types.ListCreditGrantsResponse, err error) {
+	ctx, span := telemetry.NewSpan(ctx, "list-customer-credits")
+	defer span.End()
+
+	if projectID == 0 {
+		return credits, telemetry.Error(ctx, span, err, "project id empty")
+	}
+	customerID := m.generateLagoID(CustomerIDPrefix, projectID, sandboxEnabled)
+
+	// We manually do the request in this function because the Lago client has an issue
+	// with types for this specific request
+	lagoBaseURL := "https://api.getlago.com"
+	url := fmt.Sprintf("%s/api/v1/wallets?external_customer_id=%s", lagoBaseURL, customerID)
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		return credits, telemetry.Error(ctx, span, err, "failed to create wallets request")
+	}
 
-// 	if customerID == "" {
-// 		return credits, telemetry.Error(ctx, span, err, "customer id empty")
-// 	}
+	req.Header.Set("Authorization", "Bearer "+m.lagoApiKey)
 
-// 	walletListInput := &lago.WalletListInput{
-// 		ExternalCustomerID: customerID,
-// 	}
+	client := &http.Client{}
+	resp, err := client.Do(req)
+	if err != nil {
+		return credits, telemetry.Error(ctx, span, err, "failed to get customer credits")
+	}
+	defer resp.Body.Close()
+
+	type ListWalletsResponse struct {
+		Wallets []types.Wallet `json:"wallets"`
+	}
+
+	var walletList ListWalletsResponse
+	err = json.NewDecoder(resp.Body).Decode(&walletList)
+	if err != nil {
+		return credits, telemetry.Error(ctx, span, err, "failed to decode wallet list response")
+	}
 
-// 	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 {
+		if wallet.Status != string(lago.Active) {
+			continue
+		}
 
-// 	var response types.ListCreditGrantsResponse
-// 	for _, wallet := range walletList.Wallets {
-// 		response.GrantedBalanceCents += wallet.BalanceCents
-// 		response.RemainingBalanceCents += wallet.OngoingUsageBalanceCents
-// 	}
+		response.GrantedBalanceCents += wallet.BalanceCents
+		response.RemainingBalanceCents += wallet.OngoingBalanceCents
+	}
 
-// 	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, projectID uint, name string, grantAmount int64, expiresAt *time.Time, sandboxEnabled bool) (err error) {
@@ -259,23 +288,40 @@ func (m LagoClient) CreateCreditsGrant(ctx context.Context, projectID uint, name
 }
 
 // ListCustomerUsage will return the aggregated usage for a customer
-func (m LagoClient) ListCustomerUsage(ctx context.Context, projectID uint, currentPeriod bool, sandboxEnabled bool) (usage []types.Usage, err error) {
+func (m LagoClient) ListCustomerUsage(ctx context.Context, customerID string, subscriptionID string, currentPeriod bool) (usage types.Usage, err error) {
 	ctx, span := telemetry.NewSpan(ctx, "list-customer-usage")
 	defer span.End()
 
-	if projectID == 0 {
-		return usage, telemetry.Error(ctx, span, err, "project id empty")
+	if subscriptionID == "" {
+		return usage, telemetry.Error(ctx, span, err, "subscription id empty")
 	}
 
-	subscriptionID := m.generateLagoID(SubscriptionIDPrefix, projectID, sandboxEnabled)
-	customerUsageInput := &lago.CustomerUsageInput{
-		ExternalSubscriptionID: subscriptionID,
-	}
+	if currentPeriod {
+		customerUsageInput := &lago.CustomerUsageInput{
+			ExternalSubscriptionID: subscriptionID,
+		}
+
+		currentUsage, lagoErr := m.client.Customer().CurrentUsage(ctx, customerID, customerUsageInput)
+		if lagoErr != nil {
+			return usage, telemetry.Error(ctx, span, fmt.Errorf(lagoErr.ErrorCode), "failed to get customer usage")
+		}
+
+		usage.FromDatetime = currentUsage.FromDatetime.Format(time.RFC3339)
+		usage.ToDatetime = currentUsage.ToDatetime.Format(time.RFC3339)
+		usage.TotalAmountCents = int64(currentUsage.TotalAmountCents)
+		usage.ChargesUsage = make([]types.ChargeUsage, len(currentUsage.ChargesUsage))
+
+		for i, charge := range currentUsage.ChargesUsage {
+			usage.ChargesUsage[i] = types.ChargeUsage{
+				Units:          charge.Units,
+				AmountCents:    int64(charge.AmountCents),
+				AmountCurrency: string(charge.AmountCurrency),
+				BillableMetric: types.BillableMetric{
+					Name: charge.BillableMetric.Name,
+				},
+			}
+		}
 
-	customerID := m.generateLagoID(CustomerIDPrefix, projectID, sandboxEnabled)
-	_, lagoErr := m.client.Customer().CurrentUsage(ctx, customerID, customerUsageInput)
-	if lagoErr != nil {
-		return usage, telemetry.Error(ctx, span, fmt.Errorf(lagoErr.ErrorCode), "failed to get customer usage")
 	}
 
 	return usage, nil