tierrun / tier-vercel-openai

This project generates marketing content using OpenAI, implements metered pricing model, checks for feature access based on their current subscription, reports usage of a feature, manages subscription and more using Tier.
https://tier-vercel-openai.vercel.app
MIT License
339 stars 50 forks source link

moved tier free user subscription to createUser event from session to… #18

Open jbwashington opened 11 months ago

jbwashington commented 11 months ago

… ensure only one subscription per user

Description 📣

i was having this problem where new users would be subscribed 2-3x on Stripe upon logging in and couldn’t figure out why.

The reason why is this block of code on line 23 from auth.ts in authOptions of the Metered Billing example:

    async session({ token, session }) {
      if (token) {
        session.user.id = token.id;
        session.user.name = token.name;
        session.user.email = token.email;
        session.user.image = token.picture;

        // Check if org/user already exists in Stripe, else create and subscribe to free tier
        try {
          const c = await tier.lookupOrg(`org:${session?.user?.id}`);
          console.log("Checking if user/org already exists in Tier");
          console.log(c);
        } catch (error) {
          // Auto subscribe user to the free plan if they do not have any subscription already.
          // Add OrgInfo to create/update the customer profile while subscribing
          await tier.subscribe(`org:${session?.user?.id}`, TIER_FREE_PLAN_ID, {
            info: {
              name: session?.user?.name as string,
              email: session?.user?.email as string,
            } as OrgInfo,
          });
        } finally {
          return session;
        }
      }

      return session;
    },

The duplicate subscriptions would cause some weird bugs, primarily tier.report() reporting to the wrong subscription and tier.limit() not displaying the correct usage, etc. the correct place for this try/catch block in authOptions is the createUser trigger in events:

  events: {
    createUser: async ({ user }) => {
      console.log(`User ${user.id} was created in the database....`)
      // Check if org/user already exists in Stripe, else create and subscribe to free tier
      try {
        const c = await tier.lookupOrg(`org:${user.id!}`);
        console.log(`Customer ${JSON.stringify(c)} already exists in Stripe.`);
      } catch (e) {
        // Auto subscribe user to the free plan if they do not have any subscription already.
        // Add OrgInfo to create/update the customer profile while subscribing
        console.log(
          `User ${user.id!} is a new user, subscribing to free plan....`,
        );
        await tier.subscribe(`org:${user.id!}`, TIER_FREE_PLAN_ID, {
          info: {
            name: user.name! as string,
            email: user.email! as string,
          } as OrgInfo,
        });
        console.log(
          `User ${user.id!} has been subscribed to free plan....`,
        );
      }
    },
  },

This way, it only fires on the first time the org is not found, and not every time getSession runs.

Type ✨

Tests 🛠️

Before the code change, 3 subscriptions were created, presumably because getSession was fired 3 times in the auth process before the response from prisma.user.create() returns, triggering a creation for each session call. Also, tier.report() would not increment on the client side because the wrong subscription was tied to the user. after clearing Stripe test data and db, subscription is created only once in Stripe and tier.can()/tier.report() works as expected.


vercel[bot] commented 11 months ago

@jbwashington is attempting to deploy a commit to the Tier Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] commented 11 months ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
tier-vercel-openai ❌ Failed (Inspect) Nov 17, 2023 1:26pm