Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[billing] Create Stripe invoices for teams based on their usage #10713

Merged
merged 2 commits into from
Jun 17, 2022

Conversation

andrew-farries
Copy link
Contributor

Description

Update teams' Stripe subscriptions with their usage credits during a given billing period.

Each time the usage reconciler runs, update Stripe invoices with usage credits for each team.

An example invoice:

image

(exact dollar amounts per credit are still TBD, the value is a placeholder).

Related Issue(s)

Part of #9036

How to test

  1. Open a preview environment for this PR.
  2. Create a team called 'Gitpod n', where n is the lowest number not yet used.
  3. Enable usage based billing for the new team.
  4. Start a workspace.
  5. Update the usage component to make it run more frequently:
  • kubectl edit deploy/usage
  • Edit the usage container's args field to look like:
     - args:
       - run
       - --schedule
       - 1m
  1. Wait until the usage reconciler runs (tail its logs).
  2. Visit the Stripe dashboard and find the Stripe customer that corresponds to the team you created.
  3. Click on the Customer's subscription.
  4. Check the 'Upcoming invoice' section and see that the customer has used Gitpod credits.

Every time the reconciler runs, the invoice should be updated with another credit (assuming the above interval of 1m for the reconciler).

Release Notes

NONE
  • /werft with-payment=true

Andrew Farries added 2 commits June 17, 2022 05:47
* Convert the usage report so that it contains one entry for each team
  that registered usage during the billing period.
* For each team, query Stripe to find the corresponding Customer.
* Update each Customer's subscription with their used credits.
@andrew-farries andrew-farries requested a review from a team June 17, 2022 07:20
@github-actions github-actions bot added the team: webapp Issue belongs to the WebApp team label Jun 17, 2022
@andrew-farries
Copy link
Contributor Author

andrew-farries commented Jun 17, 2022

/werft run with-payment=true

👍 started the job as gitpod-build-af-update-team-usage-in-stripe.11
(with .werft/ from main)

@jankeromnes
Copy link
Contributor

jankeromnes commented Jun 17, 2022

Epic! 🔥 I can take a look. 👀

@jankeromnes jankeromnes self-assigned this Jun 17, 2022
Copy link
Contributor

@jankeromnes jankeromnes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change looks good and works like a charm! ✨ So cool to see this all coming together.

Added a few optional comments/thoughts/questions, but please feel free to dismiss any of them and merge as is. 🚢

/hold


// Convert the usage report into a set of teamIds occurring in the report
// Convert the usage report to sum all entries for the same team.
var summedReport = make(map[string]int64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint64?

Suggested change
var summedReport = make(map[string]int64)
var summedReport = make(map[string]uint64)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using int64 elsewhere to store workspace runtimes, which already gives us way more range on the positive side than we need to store monthly usage, so I don't think a switch to uint64 is necessary.

teamIds := make([]string, 0, len(teamIdSet))
for k := range teamIdSet {
teamIds = append(teamIds, k)
summedReport[usageEntry.TeamID] += usageEntry.WorkspaceSeconds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go newbie question: There is no need to initialize new Map entries to 0 in Go? 👀 Or is this a feature of make?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, maps are initialised such that values have the default value for their type.

func FindCustomersForTeamIds(teamIds []string) {
// UpdateUsage updates teams' Stripe subscriptions with usage data
// `usageForTeam` is a map from team name to total workspace seconds used within a billing period.
func UpdateUsage(usageForTeam map[string]int64) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint64?

Suggested change
func UpdateUsage(usageForTeam map[string]int64) error {
func UpdateUsage(usageForTeam map[string]uint64) error {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

components/usage/pkg/stripe/stripe.go Show resolved Hide resolved

// workspaceSecondsToCredits converts seconds (of workspace usage) into Stripe credits.
func workspaceSecondsToCredits(seconds int64) int64 {
return (seconds + 59) / 60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's an interesting way to round up. I guess this is equivalent to something like Math.ceil(seconds / 60)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is an easy way to ensure that we bill partial minutes as full minutes.

@andrew-farries
Copy link
Contributor Author

/unhold

@roboquat roboquat merged commit 9554966 into main Jun 17, 2022
@roboquat roboquat deleted the af/update-team-usage-in-stripe branch June 17, 2022 09:36
@roboquat roboquat added deployed: webapp Meta team change is running in production deployed Change is completely running in production labels Jun 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
deployed: webapp Meta team change is running in production deployed Change is completely running in production release-note-none size/L team: webapp Issue belongs to the WebApp team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants