Skip to content

[usage] Lookup stripe customers for each team in a usage report #10674

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

Merged
merged 5 commits into from
Jun 16, 2022

Conversation

andrew-farries
Copy link
Contributor

@andrew-farries andrew-farries commented Jun 15, 2022

Description

Add a stripe package to the usage reconciler and use it to find all Stripe Customers who have registered usage in the preceding month.

By querying the Stripe API for all teamIds occurring in the usage report, we are able to find a list of Stripe Customers who should have their subscriptions updated to register usage.

This PR just finds and logs the Customers occurring in the usage report. In a subsequent PR these Customers will be updated to register their usage.

Related Issue(s)

Part of #9036

How to test

  • Get a kube context for the staging cluster (gcloud auth login then gcloud container clusters get-credentials ...) - the exact commands can be taken from the GCP console.

  • Port forward to the staging database:

kubectl port-forward svc/cloudsqlproxy 3306:3306
  • Get some Stripe test mode API keys from the Stripe dashboard and save them in components/usage/apikeys. The file should look like:
{
  "publishableKey": "foo",
  "secretKey": "bar"
}
  • Run the usage reconciler against the staging database:
DB_HOST=localhost DB_USERNAME=gitpod DB_PASSWORD=<> DB_PORT=3306 go run . run --api-key-file ./apikeys

(the env vars, including password, can be found in the server pod by running):

kubectl exec server-849f6f8895-8kct9 -- env | grep DB_

After 1 minute the reconciler will run and display output like:

{"level":"info","message":"about to make query \"metadata['teamId']:'0163ce3d-d46d-4c71-9d23-80d5e014959b' OR metadata['teamId']:'2db3d0ae-9fa2-41ed-9781-acfc7e17294b' OR metadata['teamId']:'0d39eefc-d1b7-461d-a4e5-03826b3dd054' OR metadata['teamId']:'0a6486da-b080-4f66-ba86-98f5a03374b5' OR metadata['teamId']:'14f79ba1-6e24-46de-aa80-12f63e58ea5f' OR metadata['teamId']:'7981ba4e-68fe-4d0a-afa1-e21b428bd7d0' OR metadata['teamId']:'af870004-62e5-4b44-9395-cd37abb10e2e' OR metadata['teamId']:'58ba9114-49ee-4538-8550-6f2bace66ac4'\"","serviceContext":{"service":"usage","version":""},"severity":"INFO","time":"2022-06-15T06:36:28Z"}
{"level":"info","message":"found customer \"Test Customer (for invoice generation)\" for teamId \"2db3d0ae-9fa2-41ed-9781-acfc7e17294b\"","serviceContext":{"service":"usage","version":""},"severity":"INFO","time":"2022-06-15T06:36:28Z"}

Showing the query that was run against the Stripe API and the 1 customer that was found with a matching teamId.

Release Notes

NONE
  • /werft with-payment

Andrew Farries added 4 commits June 14, 2022 14:46
Ignore `apikeys` file, used for local testing.
When the `run` subcommand is invoked, authenticate to the Stripe API
using API keys.
Build and test the query that will be used to retrieve a list of stripe
customers matching a given set of teamIds.
@andrew-farries andrew-farries requested a review from a team June 15, 2022 06:58
@github-actions github-actions bot added the team: webapp Issue belongs to the WebApp team label Jun 15, 2022
For each teamId in the usage report, query the Stripe API to find Stripe
Customer records that correspond to those teamIds.
@andrew-farries andrew-farries force-pushed the af/generate-test-invoice branch from ac10292 to 2ec4500 Compare June 15, 2022 07:38
@werft-gitpod-dev-com
Copy link

started the job as gitpod-build-af-generate-test-invoice.9 because the annotations in the pull request description changed
(with .werft/ from main)

@werft-gitpod-dev-com
Copy link

started the job as gitpod-build-af-generate-test-invoice.10 because the annotations in the pull request description changed
(with .werft/ from main)

@jankeromnes
Copy link
Contributor

jankeromnes commented Jun 16, 2022

Many thanks! Will take a look. 👀

@jankeromnes jankeromnes self-assigned this Jun 16, 2022
@@ -0,0 +1 @@
apikeys
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting -- is placing an untracked, secret file next to the cmd the recommended way to provide credentials to a Go component? 👀 (I haven't worked on Go component secrets before.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this way works well here. When the component runs in a pod it takes the credentials file in the same way (file on disk, location passed via --api-keys-file).

How would you expect the necessary api keys to be provided when running the usage component locally?

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the insight!

How would you expect the necessary api keys to be provided when running the usage component locally?

Hmm, I never run components locally -- only inside a complete Gitpod deployment (with secrets configured for Werft or staging/production)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I think @easyCZ is keen to move away from requiring preview environments to develop components. When working on the usage component I think the intention is to run it locally, port forwarding to a database as necesary.

// It returns multiple queries, each being a big disjunction of subclauses so that we can process multiple teamIds in one query.
// `clausesPerQuery` is a limit enforced by the Stripe API.
func queriesForCustomersWithTeamIds(teamIds []string) []string {
const clausesPerQuery = 10
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting. I guess you found this limit by testing. Is the value 10 documented somewhere? 👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not documented anywhere I could find. It appeared in an error message when trying larger queries.

Copy link
Contributor

@jankeromnes jankeromnes Jun 16, 2022

Choose a reason for hiding this comment

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

Thanks! No worries -- I asked for more references, because I also wonder whether the actual limit can be different between "test" and "live" mode. But 10 should work.

(I guess this will just potentially fire a lot of Stripe queries in production, because we have many teams there -- but let's see about that when it really happens.)

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, I wondered about differences between live vs staging too.

We will likely need to rate limit our Stripe API calls in production in general. For this search endpoint the rate limit is:

Rate limits
We apply a rate limit of up to 20 read operations per second which applies for all search endpoints in both live mode and test mode. Live mode and test mode limits are separate.

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.

Works like a charm, thanks! 🎉

Tested by setting up usage-based billing for a team, running a few workspaces, and checking the usage component logs (it found the customer).

@roboquat roboquat merged commit 060c4f5 into main Jun 16, 2022
@roboquat roboquat deleted the af/generate-test-invoice branch June 16, 2022 12:35
@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.

3 participants