@@ -6,18 +6,20 @@ package apiv1
6
6
7
7
import (
8
8
"context"
9
- "github.com/gitpod-io/gitpod/usage/pkg/contentservice"
9
+
10
10
"math"
11
11
"time"
12
12
13
13
"github.com/gitpod-io/gitpod/common-go/log"
14
14
v1 "github.com/gitpod-io/gitpod/usage-api/v1"
15
+ "github.com/gitpod-io/gitpod/usage/pkg/contentservice"
15
16
"github.com/gitpod-io/gitpod/usage/pkg/db"
16
17
"github.com/gitpod-io/gitpod/usage/pkg/stripe"
17
18
"github.com/google/uuid"
18
19
stripesdk "github.com/stripe/stripe-go/v72"
19
20
"google.golang.org/grpc/codes"
20
21
"google.golang.org/grpc/status"
22
+ "google.golang.org/protobuf/types/known/timestamppb"
21
23
"gorm.io/gorm"
22
24
)
23
25
@@ -31,12 +33,12 @@ func NewBillingService(stripeClient *stripe.Client, billInstancesAfter time.Time
31
33
}
32
34
33
35
type BillingService struct {
34
- conn * gorm.DB
35
- stripeClient * stripe.Client
36
- billInstancesAfter time.Time
37
-
36
+ conn * gorm.DB
37
+ stripeClient * stripe.Client
38
38
contentService contentservice.Interface
39
39
40
+ billInstancesAfter time.Time
41
+
40
42
v1.UnimplementedBillingServiceServer
41
43
}
42
44
@@ -66,7 +68,66 @@ func (s *BillingService) UpdateInvoices(ctx context.Context, in *v1.UpdateInvoic
66
68
}
67
69
68
70
func (s * BillingService ) FinalizeInvoice (ctx context.Context , in * v1.FinalizeInvoiceRequest ) (* v1.FinalizeInvoiceResponse , error ) {
69
- log .Infof ("Finalizing invoice for invoice %q" , in .GetInvoiceId ())
71
+ logger := log .WithField ("invoice_id" , in .GetInvoiceId ())
72
+
73
+ if in .GetInvoiceId () == "" {
74
+ return nil , status .Errorf (codes .InvalidArgument , "Missing InvoiceID" )
75
+ }
76
+
77
+ invoice , err := s .stripeClient .GetInvoice (ctx , in .GetInvoiceId ())
78
+ if err != nil {
79
+ logger .WithError (err ).Error ("Failed to retrieve invoice from Stripe." )
80
+ return nil , status .Errorf (codes .NotFound , "Failed to get invoice with ID %s: %s" , in .GetInvoiceId (), err .Error ())
81
+ }
82
+
83
+ reportID , found := invoice .Metadata [stripe .ReportIDMetadataKey ]
84
+ if ! found {
85
+ logger .Error ("Failed to find report ID metadata on invoice from Stripe." )
86
+ return nil , status .Errorf (codes .NotFound , "Invoice %s does not contain reportID" , in .GetInvoiceId ())
87
+ }
88
+ logger = logger .WithField ("report_id" , reportID )
89
+
90
+ subscription := invoice .Subscription
91
+ if subscription == nil {
92
+ logger .Error ("No subscription information available for invoice." )
93
+ return nil , status .Errorf (codes .Internal , "Failed to retrieve subscription details from invoice." )
94
+ }
95
+
96
+ teamID , found := subscription .Metadata [stripe .TeamIDMetadataKey ]
97
+ if ! found {
98
+ logger .Error ("Failed to find teamID from subscription metadata." )
99
+ return nil , status .Errorf (codes .Internal , "Failed to extra teamID from Stripe subscription." )
100
+ }
101
+ logger = logger .WithField ("team_id" , teamID )
102
+
103
+ attributionID := db .NewTeamAttributionID (teamID )
104
+
105
+ // To support individual `user`s, we'll need to also extract the `userId` from metadata here and handle separately.
106
+
107
+ report , err := s .contentService .DownloadUsageReport (ctx , reportID )
108
+ if err != nil {
109
+ logger .WithError (err ).Error ("Failed to retrieve usage report from content service." )
110
+ return nil , status .Errorf (codes .Internal , "Failed to download usage report." )
111
+ }
112
+
113
+ invoicedSessions := report .GetUsageRecordsForAttributionID (attributionID )
114
+ var errors []error
115
+ for _ , session := range invoicedSessions {
116
+ _ , err := s .SetBilledSession (ctx , & v1.SetBilledSessionRequest {
117
+ InstanceId : session .InstanceID .String (),
118
+ From : timestamppb .New (session .StartedAt ),
119
+ System : v1 .System_SYSTEM_STRIPE ,
120
+ })
121
+ if err != nil {
122
+ logger .WithField ("workspace_ignstance_id" , session .InstanceID ).WithError (err ).Error ("Failed to mark session as billed by Stripe." )
123
+ errors = append (errors , err )
124
+ }
125
+ }
126
+
127
+ if len (errors ) != 0 {
128
+ logger .Errorf ("Failed to mark %d sessions as billed. You have to update them manually!" , len (errors ))
129
+ return nil , status .Errorf (codes .Internal , "Failed to mark %d sessions as billed by stripe." , len (errors ))
130
+ }
70
131
71
132
return & v1.FinalizeInvoiceResponse {}, nil
72
133
}
0 commit comments