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

feat: update Stripe plan in PlansStorage#set #318

Closed
wants to merge 13 commits into from
Closed

Conversation

travis
Copy link
Contributor

@travis travis commented Jan 23, 2024

We'd like to let users change their plan directly from console or the CLI, which means we need to teach PlansStorage#set how to update Stripe.

I've introduced a new BillingProvider interface to start abstracting Stripe functionality and especially to make it easier to test systems that interact with Stripe. I have not done a thorough audit of the various ways we interact with Stripe, but I'd like to move toward having all Stripe interactions go through BillingProvider.

The linter is currently failing because I made the account field in the the billing customer schema optional and a fair amount of the billing logic relies on it being not-undefined. I could just go in and update logic to deal with undefined account but wanted to get some feedback from @alanshaw before I dig into that: I can see arguments for either option (roughly "we might want to track customers who don't have an account set up in Stripe" vs "the point of this table is to track Stripe accounts") but testing will require a bit more work if we want to preserve non-optionality (since the test stores don't get initialized from the test billing provider in the same way - in production they get initialized via the webhook handler, which doesn't currently have an analog in testing code). Either of the options here will require a bit more work, so I'd love to know if anyone has strong feelings before I finish this off.

We'd like to let users change their plan directly from console or the CLI, which means we need to teach `PlansStorage#set` how to update Stripe.

I've introduced a new `BillingProvider` interface to start abstracting Stripe functionality and especially to make it easier to test systems that interact with Stripe. I have not done a thorough audit of the various ways we interact with Stripe, but I'd like to move toward having all Stripe interactions go through `BillingProvider`.

The linter is currently failing because I made the `account` field in the the billing customer schema optional and a fair amount of the billing logic relies on it being not-`undefined`. I could just go in and update logic to deal with undefined `account` but wanted to get some feedback from @alanshaw before I dig into that: I can see arguments for either option (roughly "we might want to track customers who don't have an account set up in Stripe" vs "the point of this table is to track Stripe accounts") but testing will require a bit more work if we want to preserve non-optionality (since the test stores don't get initialized from the test billing provider in the same way - in production they get initialized via the webhook handler, which doesn't currently have an analog in testing code). Either of the options here will require a bit more work, so I'd love to know if anyone has strong feelings before I finish this off.
Copy link
Member

@alanshaw alanshaw left a comment

Choose a reason for hiding this comment

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

Please can you explain why account needs to become optional? I don't see anything in this PR that requires it.

billing/tables/customer.js Show resolved Hide resolved
upload-api/billing.js Show resolved Hide resolved
upload-api/billing.js Show resolved Hide resolved
if (hasCustomerResponse.error) {
return hasCustomerResponse
} else {
return { error: new Failure(`billing provider does not have customer for ${customer}`) }
Copy link
Member

Choose a reason for hiding this comment

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

So this would maybe be a BillingCustomerNotFoundError that we can use in the types for PlansStorage?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah yep - I want to replace all the Failures in this PR with typed error responses like that, great catch!

}
}
} catch (/** @type {any} */ err) {
return { error: err }
Copy link
Member

Choose a reason for hiding this comment

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

Just let it throw...this is what ucanto will do for us essentially.

@travis
Copy link
Contributor Author

travis commented Jan 24, 2024

Please can you explain why account needs to become optional?

For sure - tried to explain this in the PR description but it was end-of-day stream of consciousness.

Tests for PlansStorage (which live in w3up) don't currently do any setup, which means there isn't a record in the test Dynamo when we invoke plan/set. Since that doesn't currently require a full record (ie, doesn't error when it doesn't find a record) I was seeing errors in the test (errors trying to decode the account-less record in Dynamo) when it subsequently invoked plan/get.

Taken on its own, this suggests to me that we should rework the test and perhaps make plan/set fail if there isn't already a record in Dynamo, but it also made me wonder if account should be optional - it seems like we might want to track billing for a customer even if they have not yet set up a Stripe account and allow them to connect a Stripe account later.

It's also entirely possible that this case is already supported without making account optional, so I figured I'd pause and check in with you on the intent behind making account non-optional. @alanshaw I would love to huddle this morning to kick this around if you have a few minutes!

travis added a commit to storacha-network/RFC that referenced this pull request Jan 25, 2024
We had a quick huddle about `Ucanto.Result` inspired by a discussion in `w3infra` (storacha-network/w3infra#318 (comment)) and I committed to writing up a proposal describing the tradeoffs of using _only_ `Result` for error handling vs using `Result` plus normal JavaScript error handling.

I've proposed we standardize on `Result` purity and offered to take a pass through the code to ensure we are wrapping all possible thrown errors in error `Result`s but want to get feedback from the team before I do that!

Comments sincerely requested - I, for one, could pretty easily be swayed to the "error `Result`s for known errors, thrown errors for unexpected stuff" camp, so if you feel strongly please let us know!
linter still failing because I'm returning unexpected error types, need to update w3up one more time..
it gets initialized before our system, so should always have a record.
upload-api/test/helpers/billing.js Outdated Show resolved Hide resolved
Co-authored-by: Alan Shaw <alan.shaw@protocol.ai>
also respect no-implicit-coercion
found this in the organization secrets in GitHub, so let's re-use it
I was not doing it correctly, which is I think why the integration tests are failing
I updated them in the Stripe test environment to match what we use in production, since we don't support using different plan names across the board.
@travis
Copy link
Contributor Author

travis commented Jan 31, 2024

created #320 because the integration environment for this PR is broken - integration tests pass there!

@travis travis closed this Jan 31, 2024
@travis travis reopened this Jan 31, 2024
@travis travis closed this Jan 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants