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

refactor Stripe webhook #200

Merged
merged 30 commits into from
Jul 10, 2024
Merged

refactor Stripe webhook #200

merged 30 commits into from
Jul 10, 2024

Conversation

vincanger
Copy link
Collaborator

@vincanger vincanger commented Jun 28, 2024

Fixes issue #199

@vincanger vincanger changed the title rename TierIds to PaymentPlanIds refactor Stripe webhook Jul 2, 2024
@vincanger vincanger marked this pull request as ready for review July 2, 2024 14:40
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

Left comments, nice improvements!
We will also want to add some info to the docs regarding the Stripe API version, right?

template/app/src/server/stripe/stripeClient.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Show resolved Hide resolved
template/app/src/shared/constants.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@sodic sodic left a comment

Choose a reason for hiding this comment

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

Answered some of the questions/dilemmas I was tagged on.

I might look at more stuff later :)

template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
import { HttpError } from 'wasp/server';
import { emailSender } from 'wasp/server/email';

const getCustomerIdStringOrThrow = (userStripeId: unknown): string => {
Copy link
Collaborator

@sodic sodic Jul 4, 2024

Choose a reason for hiding this comment

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

Arrow functions

This is the original explanation: wasp-lang/wasp#487

But I wrote it back when we were using JavaScript (there's an important caveat for TypeScript, also explained in Effective TypeScript, Item 12).

Anyway, I am no longer sure what to advocate. I like the "important stuff on top benefit", but perhaps I'd put more weight on consistency- You'll definitely have to use arrows a lot (that's how our hooks and operations work), so you might as well use them all the time.

unknown and validation

We should do validation as early as possible (i.e., when we receive the request). As soon as we ensure something has the type we expect it to have, we should stop validating the data and just rely on TypeScript.

The rule is: Make sure something is of type T before you say it's of type T. In other words, all validation should happen before the assertion, not after it.

Therefore, this function shouldn't be doing the validation. The validation should be done before the event.data.object as Stripe.Checkout.Session expression:

  1. If receiving malformed data is considered normal (i.e., it's the user's direct input, Stripe can return the data in multiple formats, some customers don't have IDs...), we shouldn't raise an exception. We should instead parse the data and propagate the appropriate error to the user (using normal control flow).
  2. If receiving malformed isn't considered normal (i.e., Stripe accidentally broke its API), we should assert the data has the proper format and throw an exception otherwise. I assume this is the case here. To do such an assertion, you can either use a library (e.g., Zod), or write it yourself. But since already using Zod, I recommend sticking with that.

Let me know if you get stuck, I'm happy to help out!

Comment on lines 4 to 10
type UserStripePaymentDetails = {
userStripeId: string;
subscriptionPlan?: SubscriptionPlanId
subscriptionStatus?: SubscriptionStatusOptions;
numOfCreditsPurchased?: number;
datePaid?: Date;
};
Copy link
Collaborator

@sodic sodic Jul 4, 2024

Choose a reason for hiding this comment

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

I don't know the domain that well, but something about this type seems like it's another candidate for what Martin talked about here: https://github.com/wasp-lang/open-saas/pull/200/files#r1664746512

I could be wrong though.

Comment on lines 12 to 27
export const updateUserStripePaymentDetails = async (args: UserStripePaymentDetails, userDelegate: PrismaUserDelegate ) => {
let data: any = {};
if (args.datePaid) data.datePaid = args.datePaid;
if (args.subscriptionPlan) data.subscriptionPlan = args.subscriptionPlan;
if (args.subscriptionStatus) data.subscriptionStatus = args.subscriptionStatus;
if (args.numOfCreditsPurchased) {
data.credits = { increment: args.numOfCreditsPurchased };
}

return await userDelegate.update({
where: {
stripeId: args.userStripeId,
},
data
});
};
Copy link
Collaborator

@sodic sodic Jul 4, 2024

Choose a reason for hiding this comment

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

To fix this, check Effective TypeScript (2nd edition), item 21.

Also, we should find a better name for data and type it properly (without using any).

I'll help you out here since it's a little weird and the book doesn't do it optimally (I'll assume the type of data is Data):

const data: Data = {
  ...args.datePaid && { datePaid: args.datePaid },
  ...args.subscriptionPlan && { subscriptionPlan: args.subscriptionPlan },
  ...subscriptionStatus && { subscriptionStatus: args.subscriptionStatus },
  ...args.numberOfCreditsPurchased && { credits: { increment: args.numberOfCreditsPurchased } },
}

Copy link
Member

Choose a reason for hiding this comment

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

@sodic I was considering this, but was wondering if it indeed is a better solution. Maybe I have to read that item 21 which I don't ahve time for now, but I gave up on this different approach as it didn't seem to improve anything, and some people might say it is more convoluted even (I hit such resistance in the past in our PRs when I tried to do this).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, I'm fairly confident that you'll agree with this approach once you read Item 21, so I suggest we follow it.

The benefits will become more apparent when data gets a proper type.

Copy link
Collaborator

@sodic sodic left a comment

Choose a reason for hiding this comment

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

I left some comments and links to material that should make them clearer, but you might have trouble figuring out some of my suggestions.

If that happens, call me up and I'll elaborate (or we can even jump on a call).

@sodic
Copy link
Collaborator

sodic commented Jul 4, 2024

@vincanger Pro tip:

When you want to link a pull request to an issue, use GitHub's special words.

This way, once you merge the PR, the issue automatically gets closed.

In this case, you can edit your description to say "Closes #199" or "Fixes #199" instead of "addresses issue #199".

You'll see how GitHub immediately underlines it and add the PR under the issue's title

template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/server/actions.ts Outdated Show resolved Hide resolved
template/app/src/server/actions.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
@Martinsos
Copy link
Member

@sodic @vincanger I wanted to try out myself some things I was suggesting (mostly regarding consolidating types), so that I don't suggest stuff that is not doable or won't look good once it is done, and I got pulled in and ended up implementing stuff.

First I ended up with this PR: #217, and then I, on top of that one, made a bit different attempt with this one: #218 . I am not sure which one I prefer, maybe the first one, but it is tight: both are a bit clunky in some aspects, but also both work quite well I think.

Please take a look and see if there is anything useful you can use.

Also, since I will be traveling in the following days, and @sodic already got quite involved, I suggest @sodic that you take over the reviewing of this PR and drive it to the end with @vincanger . I think I covered all I could from my side, and you will be able to contribute some extra knowledge on the TS side.

Copy link
Collaborator Author

@vincanger vincanger left a comment

Choose a reason for hiding this comment

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

Thanks for all your comments and help here @sodic @Martinsos -- this was a chunky boy!

There was a lot of new insight here for me, so it took me a while to get a grasp of everything.

I merged Martin's ideas from PR #217 for consolidating types. I think we're close to being done here.

I also added docs on stripe api/client versioning. The only thing left to do is create the new app_diff but I think I will do this in a new PR at another time because of there have been so many big changes and I don't want to rush it.

template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
template/app/src/client/app/AccountPage.tsx Outdated Show resolved Hide resolved
template/app/src/server/actions.ts Outdated Show resolved Hide resolved
template/app/src/client/app/PricingPage.tsx Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripeEventHandlers.ts Outdated Show resolved Hide resolved
template/app/src/shared/constants.ts Outdated Show resolved Hide resolved
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

@vincanger left some extra comments.
Pls go through the "conversation" tab in Github and resolve all the comments that are still open but can be resolved!

opensaas-sh/blog/src/content/docs/guides/deploying.md Outdated Show resolved Hide resolved
opensaas-sh/blog/src/content/docs/guides/deploying.md Outdated Show resolved Hide resolved
opensaas-sh/blog/src/content/docs/guides/deploying.md Outdated Show resolved Hide resolved
opensaas-sh/blog/src/content/docs/guides/deploying.md Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripe.ts Outdated Show resolved Hide resolved
template/app/src/server/webhooks/stripePaymentDetails.ts Outdated Show resolved Hide resolved
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

Nice, we got it all in a good shape!

@vincanger
Copy link
Collaborator Author

Nice, we got it all in a good shape!

updated docs and app_diff as well

@vincanger vincanger merged commit 78a9189 into main Jul 10, 2024
1 check passed
@vincanger vincanger deleted the refactor-subscription-logic branch July 10, 2024 14:08
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.

4 participants