@@ -7,7 +7,6 @@ package stripe
77import (
88 "context"
99 "encoding/json"
10- "errors"
1110 "fmt"
1211 "os"
1312 "strings"
@@ -51,26 +50,14 @@ type UsageRecord struct {
5150 Quantity int64
5251}
5352
54- type StripeInvoice struct {
53+ type Invoice struct {
5554 ID string
5655 SubscriptionID string
5756 Amount int64
5857 Currency string
5958 Credits int64
6059}
6160
62- type CustomerKind string
63-
64- const (
65- User CustomerKind = "user"
66- Team = "team"
67- )
68-
69- var (
70- // ErrorCustomerNotFound is returned when no stripe customer is found for a given user account
71- ErrorCustomerNotFound = errors .New ("invalid size" )
72- )
73-
7461// UpdateUsage updates teams' Stripe subscriptions with usage data
7562// `usageForTeam` is a map from team name to total workspace seconds used within a billing period.
7663func (c * Client ) UpdateUsage (ctx context.Context , creditsPerTeam map [string ]int64 ) error {
@@ -82,16 +69,13 @@ func (c *Client) UpdateUsage(ctx context.Context, creditsPerTeam map[string]int6
8269
8370 for _ , query := range queries {
8471 log .Infof ("Searching customers in Stripe with query: %q" , query )
85- params := & stripe.CustomerSearchParams {
86- SearchParams : stripe.SearchParams {
87- Query : query ,
88- Expand : []* string {stripe .String ("data.subscriptions" )},
89- Context : ctx ,
90- },
72+
73+ customers , err := c .findCustomers (ctx , query )
74+ if err != nil {
75+ return fmt .Errorf ("failed to udpate usage: %w" , err )
9176 }
92- iter := c .sc .Customers .Search (params )
93- for iter .Next () {
94- customer := iter .Customer ()
77+
78+ for _ , customer := range customers {
9579 teamID := customer .Metadata ["teamId" ]
9680 log .Infof ("Found customer %q for teamId %q" , customer .Name , teamID )
9781
@@ -112,6 +96,27 @@ func (c *Client) UpdateUsage(ctx context.Context, creditsPerTeam map[string]int6
11296 return nil
11397}
11498
99+ func (c * Client ) findCustomers (ctx context.Context , query string ) ([]* stripe.Customer , error ) {
100+ params := & stripe.CustomerSearchParams {
101+ SearchParams : stripe.SearchParams {
102+ Query : query ,
103+ Expand : []* string {stripe .String ("data.subscriptions" )},
104+ Context : ctx ,
105+ },
106+ }
107+ iter := c .sc .Customers .Search (params )
108+ if iter .Err () != nil {
109+ return nil , fmt .Errorf ("failed to search for customers: %w" , iter .Err ())
110+ }
111+
112+ var customers []* stripe.Customer
113+ for iter .Next () {
114+ customers = append (customers , iter .Customer ())
115+ }
116+
117+ return customers , nil
118+ }
119+
115120func (c * Client ) updateUsageForCustomer (ctx context.Context , customer * stripe.Customer , credits int64 ) (* UsageRecord , error ) {
116121 subscriptions := customer .Subscriptions .Data
117122 if len (subscriptions ) != 1 {
@@ -143,37 +148,55 @@ func (c *Client) updateUsageForCustomer(ctx context.Context, customer *stripe.Cu
143148 }, nil
144149}
145150
146- // GetUpcomingInvoice fetches the upcoming invoice for the given team or user id.
147- func (c * Client ) GetUpcomingInvoice (ctx context.Context , kind CustomerKind , id string ) (* StripeInvoice , error ) {
148- log .Infof ("Fetching upcoming invoice of customer. (%q %q)" , kind , id )
149- query := fmt .Sprintf ("metadata['%sId']:'%s'" , kind , id )
150- searchParams := & stripe.CustomerSearchParams {
151- SearchParams : stripe.SearchParams {
152- Query : query ,
153- Expand : []* string {stripe .String ("data.subscriptions" )},
154- Context : ctx ,
155- },
151+ func (c * Client ) GetCustomerByTeamID (ctx context.Context , teamID string ) (* stripe.Customer , error ) {
152+ customers , err := c .findCustomers (ctx , fmt .Sprintf ("metadata['teamId']:'%s'" , teamID ))
153+ if err != nil {
154+ return nil , fmt .Errorf ("failed to find customers: %w" , err )
156155 }
157- iter := c . sc . Customers . Search ( searchParams )
158- if iter . Err () != nil || ! iter . Next () {
159- return nil , ErrorCustomerNotFound
156+
157+ if len ( customers ) == 0 {
158+ return nil , fmt . Errorf ( "no team customer found for id: %s" , teamID )
160159 }
161- customer := iter .Customer ()
162- if iter .Next () {
163- return nil , fmt .Errorf ("found more than one customer for query %s" , query )
160+ if len (customers ) > 1 {
161+ return nil , fmt .Errorf ("found multiple team customers for id: %s" , teamID )
162+ }
163+
164+ return customers [0 ], nil
165+ }
166+
167+ func (c * Client ) GetCustomerByUserID (ctx context.Context , userID string ) (* stripe.Customer , error ) {
168+ customers , err := c .findCustomers (ctx , fmt .Sprintf ("metadata['userId']:'%s'" , userID ))
169+ if err != nil {
170+ return nil , fmt .Errorf ("failed to find customers: %w" , err )
164171 }
172+
173+ if len (customers ) == 0 {
174+ return nil , fmt .Errorf ("no user customer found for id: %s" , userID )
175+ }
176+ if len (customers ) > 1 {
177+ return nil , fmt .Errorf ("found multiple user customers for id: %s" , userID )
178+ }
179+
180+ return customers [0 ], nil
181+ }
182+
183+ // GetUpcomingInvoice fetches the upcoming invoice for the given team or user id.
184+ func (c * Client ) GetUpcomingInvoice (ctx context.Context , customerID string ) (* Invoice , error ) {
165185 invoiceParams := & stripe.InvoiceParams {
166- Customer : stripe .String (customer .ID ),
186+ Params : stripe.Params {
187+ Context : ctx ,
188+ },
189+ Customer : stripe .String (customerID ),
167190 }
168191 invoice , err := c .sc .Invoices .GetNext (invoiceParams )
169192 if err != nil {
170- return nil , fmt .Errorf ("failed to fetch the upcoming invoice for customer %s" , customer . ID )
193+ return nil , fmt .Errorf ("failed to fetch the upcoming invoice for customer %s" , customerID )
171194 }
172195 if len (invoice .Lines .Data ) < 1 {
173- return nil , fmt .Errorf ("no line items on invoice %s" , invoice .ID )
196+ return nil , fmt .Errorf ("no line items on invoice %s for customer %s " , invoice .ID , customerID )
174197 }
175198
176- return & StripeInvoice {
199+ return & Invoice {
177200 ID : invoice .ID ,
178201 SubscriptionID : invoice .Subscription .ID ,
179202 Amount : invoice .AmountRemaining ,
0 commit comments