Now let's go ahead and let's create the final module for our app which is turning this into a software as a service using LemonSqueezy. So let's go ahead inside of our .environment.local and let's prepare everything we are going to need from LemonSqueezy. So we are going to need the LemonSqueezy store ID, we are going to need the product ID, we are going to need the API key, and We are going to need the API key and we are going to need the webhook secret. So this is what we are going to need to obtain and we are also going to use this next public app URL, so make sure you have that as well. So go to lemonsqueezy.com and create an account.
Once you've created your account, let's go ahead and let's create our first store. So I already have a store here, so I'm going to go here to the bottom and I'm going to click add a new store. And perhaps this is the screen you are seeing initially. Let's go ahead and give our store a name. So for me, this is going to be the finance tutorial.
And let's go ahead and create the store URL as well. Let's go ahead and select the country where you are from and create a new store. And now let's go ahead and let's go inside of our settings stores, so right here and find the one you just created and this is the store ID, this number right here. So I'm going to go ahead and assign the store ID. So the next thing we need is the product ID.
So let's go ahead and do that. So I'm going to go ahead inside of store, inside of product here and I'm going to create my product. I'm going to call this a membership. If you want you can give this a description and go ahead and select subscription. Inside of here you can go ahead and select a price.
And inside of here you can select the interval one month for example. You can also offer free trial, you can also change to be metered by usage, you can also include a setup fee. For the tax category it really doesn't matter for the tutorial but it would be software as a service. So during the confirmation process when you go into production you will be contacted by Lemon Squeezy and then you you will be able to ask them stuff like this if you are uncertain. If you want to add some media or files to be accessed once the user has membership you can do that here as well.
One thing I'm going to turn off is this, display product on storefront. That's because we need metadata and storefront cannot transfer metadata. And now let's go, you can also modify the confirmation model if you want to, you can also modify the email receipt if you want to. But let's just publish the product for now. Like this.
There we go. And now you can go ahead and right click and you can copy the variant ID. So that's the one we need. So go ahead and paste the variant ID here. And if you want to change the currency you can go inside of settings here, general, and you can change the settings.
For example, US dollars. You can also enable tax inclusive pricing so that tax is calculated in the final price like this. And let's just click Save Changes. You also need to add an email address here before you save changes, so make sure you do that. There we go.
You can also play around with accepting Pay, Apple Pay, Google Pay and things like that. Let's go ahead now. And let's obtain the following which is our API key. So for that I want to go into settings. API and I already have a bunch of them.
Let's create a new one called finance tutorial and click create API key. Copy it and paste it inside of here. There we go. And now we need to obtain the webhook secret. So let's go inside of webhooks.
Let's create a new webhook here. For now let's go ahead and let's just make this localhost 3000 subscriptions webhook. So for now obviously this will not work because they won't be able to access localhost. The sign in secret here will be secret. Obviously in production this would be a secure generated string.
And now for the orders we want to listen, we want subscription created and we want subscription updated. And let's click Save webhook. There we go. And now that we know our secret we can paste it here. There we go.
So now let's go ahead and let's create our util for LemonSqueezy. So we're gonna have to install a package. So let's go inside of the terminal here. Let me close this and let's do bun add lemon squeezy slash lemon squeezy dot JS. And let's go ahead inside of our libs folder and let's create an ls.ts file.
Inside of here let's import from LemonSqueezy.js LemonSqueezySetup and in here we're going to export const setup lemon to be a very simple LemonSqueezy.setup So what I'm doing here you can let's do it like this. So open a function and make sure you return. This is important. Return lemon squeezes setup, API key, process.environment, and copy directly from here. So you know, you didn't add a misspelling like this.
There we go. So now Let's go ahead inside of our schema and let's create the subscription model. The subscription entity, right? So export const subscriptions are going to be pgTable subscriptions, subscriptions like this. Subscriptions like this.
ID will be text ID, dot primary key. User ID will be text user underscore ID, not null and unique. Subscription ID will be text, subscription underscore ID, not null and unique as well. And status will be text, status and not null either. Let's go ahead and save this file.
Let's go inside of the terminal and let's do bun run database generate and bun run database migrate. There we go. And now let me just do bun run dev before I close this. Now we're going to go ahead and we're going to create an API for our subscriptions. So let's go ahead inside of our app folder API route and inside of here we're going to chain subscriptions and we're going to add subscriptions.
So we don't have them yet. So let's go inside of here, subscriptions.ts. Let's import Hono from Hono. And let's define our app to be new Hono and let's add the slash current with the Clark middleware and an asynchronous controller which will return c.json subscription true for now. And let's export default app.
Now go back and you will be able to import rom.slash subscriptions Subscriptions, there we go. So now if you try and go to localhost 3000 slash API subscriptions There we go, I think I misspelled something. Subscriptions. Let's see, oh, so subscriptions slash current, my apologies. There we go, subscription, true.
So this is the full API route right here. Great, so we have that working. What I want to create now is I want to check if we have a active subscription or not right here. So for that, we can go ahead and speed things up by just copying the auth thing here. Let's import get auth from HonoClarkAuth.
And then let's go ahead and find the subscription by using await database from database slash drizzle, select from subscriptions from database schema. So make sure you've added these two. Where equals subscriptions.userid without.userid. So just make sure you add equals here from a drizzle ORM. There we go.
And inside we're going to return data, subscription or null so we don't return back undefined. Great, so now if I refresh I should be getting a null here because subscription does not exist yet. So now let's go ahead and let's create a post request to check out. So that's also going to have the Clark middleware. This is going to be an asynchronous controller.
Let's go ahead and reuse the ALF here. And let's go ahead and again get our subscription here but let's call it existing here so it's easier to work with so existing So if we have existing subscription ID, in that case, what we are going to do is we are going to get const subscription to be await get subscription from LemonSqueezyJS. So that's why we needed to store subscription ID in our database. So that now inside of here, we can pass existing subscription ID. And now inside of here, we have everything necessary for this, right?
All the info about subscription. So now we can generate a portal URL. So if user is trying to check out, right? And we already have a subscription in the database. That means that we are not going to redirect them to the checkout link but instead to the portal URL where they will manage their subscription.
So we're gonna get subscription.data?data.attributes.urls customer portal. Let me just close this So this is how it looks like in one line. And then we're gonna do it. There is no portal URL. We are going to return c.json, error, internal error, because this should not happen, right?
And finally, let's go ahead and let's break this function early by returning a back data portal URL. Great. Now let's go outside of this if clause and let's this time create the checkout. So constant checkout is going to be away, create checkout again from lemon squeezy right here so let me just collapse these two elements create checkout and the second argument is going to be the product id. So obviously if you had multiple products you would modify this checkout to accept the parameter of what the user is trying to purchase, right?
But we only have one, so we can just store it in the environment variable here and we can just paste it here. Now let's go ahead and add some settings here. So checkout data. This is equivalent to metadata in Stripe. So in order for us to know which user the webhook we will receive, we need to pass this.
Otherwise it's not going to work. So use the underscore case here. Don't do user ID because it gets lost. So use user ID out dot user ID like this. And now let's add the product options.
And let's add the redirect URL to simply go back to our original page where we came from. So that will be the process.environment and our next public app URL. Like this and a slash at the end. Great, so we have this. And now that we have the checkout, let's go ahead and do if, actually let's extract the checkout URL from checkout.data, data, attributes, URL.
If there is no checkout URL, we are going to return back c.json error internal error because we need to have this. Otherwise, we bring back data checkout URL. There we go. So let's go ahead now and let's create the equivalent hooks for this so we can try it out. So I'm gonna go ahead and create use checkout subscription.
Go inside of features here. Let's create a new folder subscriptions. Create an API and inside use checkout subscription and since this is a mutation we can copy the one from played for example use create link token this is the closest one right So let's just modify this to be subscriptions.current, like this. And I think, not .current, my apologies. This will be subscriptions.checkout.
A POST request with a 200, right? That's the response we are looking at. And this will be use checkout subscription. So this will not call played, but instead it will call subscriptions.checkout. So Inside of here, we will say, failed to create URL.
Instead of here, we can, we don't have to say anything because this will redirect. We are just care about the errors. So failed to create URL. No need to success because if success happens, we're going to redirect the user, right? But I don't want to do that here.
If you want to, you can do it on success here, but I'd rather do it explicitly where I call this method, right? All right. And now let's go ahead and let's use that. So let's go inside of the app folder, dashboard, settings. Let's go inside of the settings card right here.
And what I want to do is I want to go and copy this alongside with the separator, right? So starting with the separator, everything inside of the card content here. And this time we're going to call this subscription. Like this. And let's pretend like we have a subscription.
So I'm going to write const subscription. Will be, well, nothing for now. Let's explicitly adjust right now, do not have any problems here, right? So if we don't have subscription is going to be muted we will add this and this will just say Subscription active and this will say no subscription active. And this will again look at subscription.
So we are interested in this one, right? Because this one will be used to remove the subscription and this one will be used to add the subscription. So this will be subscription checkout component. So let's go ahead and create it. It's going to be very simple.
So go inside of the features, subscriptions. Let's create a new folder components and inside let's create a subscription checkout DSX. Let's import use checkout subscription from features subscriptions API And let's also import our button, not from React Day Picker, but from components UI button. Let's export const subscription checkout. No props received here.
Let's get our checkout mutation, use checkout subscription and let's return a button here which will say upgrade. Let's go ahead and give it an on click here. Checkout.mutate disabled, checkout is pending, variant will be ghost And size will be small like this. There we go. And now that I think about it, we can actually go inside of the used checkout subscription and let's add the onSuccess here.
And inside of here we can destructure our data now. So our data should be a string, right? The portal URL or the checkout URL. So let's just do window.location.href to that data. So we handle that here and we don't have to worry about it anywhere else.
Great. So now we have this and this will automatically redirect the user. Great. So let's go ahead and let's do the following. Let's import our subscription checkout and let's use it here as well, right?
It doesn't matter. And let me just move the feature up. But separate it like this. Now let's go ahead and try this out. So I'm gonna go inside of the local host here and let me just see if this will load, okay.
So we have some hydration error. I suppose that's from clerk And let's get inside of settings here. And there we go, no subscription active, but when I click upgrade, all right, we get failed to create URL. Let's see why that happened. So it just says 500 here.
Let's see what's going on. So I'm gonna go inside of the subscriptions. Right here. And we can go ahead and check out the one that's wrong. So I suppose it's this one.
Let's go ahead and console.log checkout to see what's going on here. So I will click upgrade again. All right, so we have a message missing API key and that's because I forgot to add one important thing. So inside of this subscriptions API route, we need to import setup-lemon from libls and simply call it here. Like that.
Let's try this again now. So I'm gonna go ahead and click upgrade. And there we go. I'm being redirected and here I am I now have my membership $9 every month right here but before we can try this out we have to create our webhook otherwise it's not going to work. So let's go ahead and create our webhook.
So we're gonna do that here in subscriptions.ts and let's go ahead at the bottom so I'm gonna close everything that exists So get post like this and let's create another post method slash webhook. This one is not going to have any middleware because it needs to be publicly available. So we have to use the signature to confirm that it's LemonSqueezy who is trying to access this. So we need to access our data in form of text. So wait, c.request.text.
Then let's go ahead and create everything we need to confirm that the signature comes from the origin we accept. So we start by creating an HMAC here using crypto.createHMAC. SHA256 is the encryption key here. Then the encryption method sorry and then we're gonna add process.environment LemonSqueezyWebhookSecret So that's why this should be unique. So only you and Lemon Squeezy will know about it.
And let's go ahead and import crypto from crypto. I think that this comes with node. So you don't have to do npm install crypto. It will simply exist, right? Great, so we have this.
Now let's create the digest. So that's going to be buffer from, in the first argument, hmac.update, text, digest, into hex, like this. And the second argument will be utf-8 like this. There we go. And finally we get the signature which will be buffer.prom again c.request.header x-signature as string and utf-8 So you can find all of this information in the LemonSqueezy documentation.
So I loaded up the docs right here. So I think inside of here here you are. So you have the example payload, the webhook requests and you also have signing requests. So to ensure that webhook requests are coming from LemonSqueezy this is what you have to do and here we have the Node.js example. So I slightly modified it so it fits our Hono.js example here.
So now we have this signature here and now we have to check if it matches so we are going to do if not crypto dot timing is set equal between digest and the signature In that case we are going to return C dot JSON error unauthorized with a status of 401. Like this. Otherwise let's get the payload. So const payload will be JSON parse text, and then we can get the event. So event will be payload.meta event underscore name like this.
And then we can destructure the subscription ID from payload.data.id. We can destructure the user id from payload.meta.custom underscore data dot user underscore id because if you remember our checkout here That is exactly how we add the user ID here. All right, so we have the user ID and what we need is the status of the subscription. So the cool thing about LemonSqueezy is that it takes care of that for us. So we don't have to do the comparison of dates or anything.
We are simply going to follow the status which can be active, pending, expired, canceled, all of those things. So if the event is subscription underscore created, in that case, we have to insert into the database a subscriptions. Let me just see if we have subscriptions here. So subscriptions, we should have it subscriptions and let's just confirm we have it in our database schema. Great.
So inside of here, let's add values. ID will be create ID from our cuid package. So just make sure you've added this. There we go. Subscription ID will be passed, user ID and the status.
All of those things will be passed right here. And now let's copy and paste this. And now we're going to say if subscription underscore updated. In that case, we're going to use the update subscriptions and set, but the only thing we're going to change is the status. And not all of them, but only those where it's the subscriptions.userId that matches the auth.userId.
So only for this user are we going to change. And not auth.userid, my apologies, subscriptions.subscriptionid and subscriptionid, which we have destructured here. Right, so that's what we are changing here. There we go. And what's important, very important, is that you return a successful message here.
So make sure you mark this as 200. There we go. That is our webhook and now we have to get it running. So I'm going to be using ngrok for that. So make sure you have your localhost running and then go ahead and run ngrok http 3000.
If you don't have ngrok you can Google ngrok and set it up. I also have a guide in my Twitch clone if you haven't installed it before. And now you need to get this HTTPS URL right here and that's going to be your webhook URL. So our webhook goes slash subscriptions slash webhook. So let's go ahead and revisit our lemon squeezy webhook here, modify it.
And we're going to change this part with our new ngrok slash subscriptions slash webhook. There we go. We added subscription created, subscription updated, we have the secret. Let's click save webhook right here. And let's just confirm that this is the correct one.
So I'm just gonna go ahead. Can I copy this from here? Let me just see if this works. So this should just load my root localhost. There we go.
Login screen, perfect. Great, so we have this and yeah make sure that in your middleware you are not protecting the API routes so that's crucial otherwise the webhook will not be able to get access. So I know we said that we won't protect it, but they also gave you the liberty of doing that if you like it. Well, for Webhook, you must not do that. All right, let's see how this will work.
Will it work at all? So go ahead inside of your terminal and let's keep the ngrok open so that we can see any HTTP requests happening here. Now let's go inside of here and let's go ahead and click upgrade. And now we're going to use a test card, which is 4242424242, like this. So same as Stripe.
Enter anything for this, just make sure it's in the future and go ahead and just enter any information here It doesn't matter. And let's go ahead and click pay. And now we're going to see whether our webhook has fired or not, and whether our subscription has been created in the database. So let's click continue. And let's go ahead right here.
Let's refresh the webhooks and now we'll see if this worked or not. Let's see inside of my products here. I do have a purchase. Let's see by whom. So there we go.
But my webhook, there we go. So there's something wrong with my webhooks. All right, so it says 404. So we're gonna have to modify this. So slash subscriptions slash webhook.
Is that not, oh, it's not, my apologies. So I completely forgot slash API slash subscriptions. Let's save the webhook and let's try this again. So even better because one thing I forgot was that I want to have my database studio running so that I can see the subscription being created. So right now I don't have any subscriptions here.
Let's go ahead and let's purchase one from the settings. So upgrade right here. Let's go ahead and enter our dummy data here. All right. And let's pay And let's see if this will work now.
There we go. Thanks for your order. Continue. And let's go inside of here first And let's see if there was any new subscription here All right, So what happens now is that we have an existing subscription and it's trying to update it, but let's see, where is it going here? I'll just zoom out so we can debug.
Yeah, it's still using the old webhook. Alright so this is how I want to resolve this. I want to go inside of home and I'm going to remove this product. Right, we can do that because we are still in development mode. Right, So let's remove this product entirely and let's create a new one.
Let's call it membership again. Let's go ahead and select a subscription. Let's change that to 19 just so we know it's the new one and let's change it to every month. SAS and remove it from the storefront and publish the product. Let's try this again now.
I'm gonna go ahead and copy this. And I'm gonna click copy variant ID. So that's important. Not ID, variant ID. Let's go back inside of our .environment.local here and find the product ID and change it.
And let's see if we can try this again now so let me just confirm that my settings webhooks has actually changed API subscriptions webhooks so this will keep retrying, right? It has a retry system, but you know, it's okay for now. This is just a tutorial. Let's go ahead and do this again. There it goes, the new one, 19.
So I think that now It should work. Let's see. Croatia. And let's click pay. So this is a new subscription.
If it's still not working, we're just going to, well, we'll think of something. It can be resolved. Let's go ahead and see if any news, webhooks have fired here. So this is from before. Let's keep refreshing to see.
All right, so this is, I think this is still reflecting the old subscription. Let me see. What is the price on this one. I can't see the price on this so I'm not sure if this is the old one or the new one. But you can see that it's changed to 500.
And I think I know why it changed to that, but I just want to see what is it with the newer ones. All right, so in here we still have 500 it seems. Let's see what went wrong. So something is still not working here. Let's see what.
There we go. Here is the error. Let's see. Cannot read properties of undefined reading user ID. Okay.
Let's go ahead and see what that is about. So I'm gonna go ahead inside of subscriptions here and I'm gonna see what's going on here. So we have meta customer data that is supposed to be custom data. My apologies. So this is custom data.
So what you can do now, well, what I want to show you is not exactly reliable. So here's what I want to do. I want to improve our webhook because I just noticed that we need to somehow think of both of these as being able to both create the subscription for the first time or update it. So that's what I want to do. I'm gonna do this const existing let me zoom in let's do await database dot select everything from our subscriptions where equals subscription our subscriptions where equals subscriptions.
Subscriptions. Subscription ID and the subscription ID, which we destructured. So this is the existing. So what we're gonna do here is we're gonna check if we have existing. In that case, we are going to update, right?
So this code. Else we are going to create this code, right? And we can do the same thing for this as well. It can be exactly the same. So this way we have fail safes if something goes wrong.
So I think that perhaps now that I've updated this, maybe a subscription has been fired here. All right let's just ignore these we don't care about these anymore. Let's go ahead and remove our product again and let's try one more time. So I'm gonna create a new product here. Membership subscription.
Well let's do 29 this time every month, SaaS, remove this and publish the product. Let's go ahead and copy the variant ID here. Let's go inside of .environment.local and let's replace the product ID. There we go. We're gonna try this again.
Let me just confirm that I don't have a subscription in my database. So no subscriptions here. Let's go inside of settings and let me keep my terminal open to catch any 500 errors if they happen. So I'm going to press upgrade here, the webhooks are running. And let's see if perhaps now with our new failsafe, we've made an improvement perhaps.
So I'm going to say Croatia here won. And let's pay. And judging by this post call it looks like we got a 200 success message. So that should mean that in my database I now have my subscription. Here it is and it has an active status.
Let's go ahead and check our webhooks here. And finally a working webhook, Subscription created. Great! So we have a failsafe and now we don't have to worry. Great!
So now what I want to do is I want to create the use subscription, use get subscription hook so let's go ahead inside of our features and we can copy a random use get categories for example paste it inside of here and let's call it use get subscription use get subscription let's change this to subscriptions dot current fail to fetch subscription and the query key is subscription like this, there we go So now let's go ahead and let's go inside of our, where should we go? Inside of the settings card here, go ahead and copy this and paste it and add use get subscription from features subscriptions API use get subscription. There we go. Now this will be current, let's call this subscription. So we can remove this constant here and this will be isLoadingSubscription, Like that.
So these errors are going to go away now. And what we can do here with the subscription is we can turn this into this, and then we can directly read from subscription status. Like that. So even if we cancel it, it's going to say canceled. There we go.
Subscription active. Perfect. And now what we can do is we can just simply render the subscription checkout here and we can reuse this. Let's also add, so it's either loading any of these two. We're going to display that loader for the subscriptions.
Now let's go ahead and copy this and let's put both of that inside subscription checkout here. So let's add it here. So use a get subscription. From features subscriptions API. And now I'm going to go ahead and change the text depending on if we have it so if we have it is going to be manage otherwise upgrade and let's disable it if it is loading.
There we go. So now it says manage. Let's see if our function is working to change this. So there we go. Now I should be here in the subscription, but it looks like it says no active subscriptions.
I believe that is some form of bug here. And I think it is because I am logged in as another user here. So I think you might actually see your subscription, but I'm logged in as a specific user here. So that's why I'm seeing this. So let's just go ahead and see if there's something I can do here.
Just a second. All right, so I'm unfortunately logged in as another user and I can't log out without logging out from my main dashboard for now. So I'm just going to keep it like this and now we have the working subscription thing. So what I want to build now is I want to build a subscription model which we are going to use as a paywall, right? So let's go ahead and do that but before we do that I just want to revisit the app API subscriptions here so if existing set status if existing set status okay yes okay.
I think this will work fine. I was just interested in what we put here. Okay, that's fine. Let's go ahead and let's first create a hook to control this model So we're gonna go ahead and create inside of features Subscriptions here We're gonna create hooks and this one will be called usepaywall.ts And we're also going to need to create a store And this one will be called useSubscriptionModel.ts. And let's just copy from, Can we copy from transactions here?
Use new transaction here. Yeah, let's actually keep this in hooks. My apologies, I didn't know why I created a store. So we're gonna have these two. First, let's do use subscription model.
So this is going to be subscription model like that and the rest can stay the same. And now let's go inside of the components and let's create the subscription model. So let's go ahead and export const subscription model here. Let's go ahead and get our checkout to be use checkout subscription. I'm going to change this to use features subscriptions API like this.
Let's go ahead and let's use use subscription model. Again, I'm going to change this to Features, Subscriptions. And from here we can see the structure is open and on close. Now Let's go ahead and let's return our dialog, but I'm not sure if we have a dialog. Let's see.
We do have a dialog. All right. Let's see where do we use the dialog so I can remember. We use the dialog in use select account. Great.
So we do have the dialog. So let's go ahead and let's just import everything we need from the dialog. So dialog, dialog content, description, footer, header and title. Let's also add the separator and let's also add a button. Let's also add at the top two imports, one for image and one for the check circle from Lucid React.
Great! And now let's go ahead and create this model. So dialogue will have the property open, on is open on open change will be on close let's add the dialogue content Let's add a dialog header class name flex-items-center and space y4 Now inside of here I want to add an image and this image will have the following properties. So source will be logo.svg, alt will be logo, width will be 36 and height will be 36. So depending on what logo you have, you might need to create an alternative dark version because this is on a white background.
So for example, my logo is white, right? So what I'm going to do, and what you can do if you use the same logo in SVG form is copy it, and let's call this logo dark.svg, so logo-dark, and I'm simply gonna find the white colors and I'm gonna change them to dark colors. So now if I open Logo Dark, there we go. I have a dark logo or you can simply copy it from the source code. So let's go ahead and use Logo Dark here like this.
Great! And below that let's add a dialogue title Upgrade to a paid plan and let's give it a class name of TextCenter and now add a dialogue description which will say upgrade to a paid plan to unlock more features and give it the class name of TextCenter and now below the separator, below the header we're going to add a separator like this and inside of here we're going to create an unordered list with a class name of space Y2. And inside of the unordered list, we're gonna create a list of features that the user will unlock by purchasing this. So a list element, flex-item-center, and inside, check-circle-2, with a class name, size-5, margin-right of 2, fill-blue-500, and text-white, Like this. So let me go ahead and collapse these elements.
Well, just the class name actually. And then we're going to say a paragraph bank account syncing with class name, text SM and text muted foreground. And Now we can copy and paste these elements as many times as we need. So the second upgrade will be upload CSV files. And you can put anything you wish to paywall, right?
So I'm also going to change different chart types. There we go. And now outside of dialog content, sorry inside of dialog content, add a dialog footer with a button component. Upgrade. Let's give this a class name of PoolWidth and let's go ahead and give the dialog ptr a class name of PaddingTop2, MarginTopOf4 and GapYOf2 And let's go ahead and let's add two of our, oh, we already have checkout, great.
So what we can do here is we can write on click checkout.mutate. And that will automatically redirect the user and disabled if checkout is pending. There we go, great. So now let's go inside of our sheet provider And let's add the subscription model here. So it's not exactly a sheet, but it kind of fits in this entity, right?
There we go. So we have the subscription model now. And now let's go ahead and let's create our usePaywall hook. So that we can finally paywall all the things we need. So I'm gonna go ahead inside, let me just close this.
Where am I? Features, subscriptions, hooks, use paywall. So we are going to import useGetSubscription and useSubscriptionModel from Hooks right here. So our useGetSubscription and our useSubscriptionModel, right? These two which we just created and this is just TypeScript playing around all right and now in here let's export const use paywall const subscription model will be use subscription model So const subscription model like this and const use get subscription.
Data will be subscription is loading subscription. And then we're going to create const should block. So if we don't have subscription, that means we should block this feature. But if we do have subscription, we should check for the status. And we are only going to block it if the user's status is expired, right?
So I don't want to, you could do, for example, you might be thinking, why wouldn't I just do if not active? Well, that's because, for example, someone can purchase a subscription for a month and cancel immediately. They still paid you so you need to provide them with 30 days access. So that's why this is a bad idea. Right?
That's why I rather confirm that if a subscription status is expired, that means that their period has ended. Whatever they paid for has ended. Right? LemonSqueezy by default also offers a dunning. So if you go inside of LemonSqueezy here and go inside of your settings, let's go inside of general here, you have recovery here.
I think there we go. And in here you have dunning. So if you want to you can turn this off. But before the payment expires, it will become unpaid and it will send the user a series of emails to remind them to pay. If you want to you can turn this off or you can reduce this to I don't know two or five days.
Great! So that's why we are doing this logic here and let's return isLoading to be isLoadingSubscription shouldBlock and let's do triggerPaywall to be an arrow function which we'll very simply call the subscription model on open. There we go! So now let's go ahead and use this. But before we do that, I wanna go inside of my Drizzle Studio here, Or actually you can either delete the subscription from here or you can log in into a user which doesn't have the subscription.
So I'm gonna do that. I'm gonna go into another user here. Alright, so here I am a completely empty user with no active subscription and I want it to stay that way. So now let's go ahead and let's use this wherever we need. So First things first, we have the upload button, right?
And we have a to do, add a paywall. So this is where the upload button is located. So inside of the app folder, dashboard, transactions, upload button. Let's go ahead and let's add our paywall. So we're gonna go ahead and do the following.
Just a second. So we are going to use use paywall from features subscriptions use paywall and let's destructure the should block and trigger paywall. And then this is what we're gonna do. If should block, we can't do this dynamically here. So we can just copy this.
Like there's no nicer way of doing that because this button has a wrapper here. So we are just going to do this directly and we are going to remove this and we're going to add on click to be trigger paywall instead. And make sure you add a return here. Like this. Let's try it out now.
So I don't have a subscription. So if I go inside of transactions now, and if I click import, there we go. Upgrade to a paid plan. And when I click upgrade, I should be redirected and I am. Perfect!
And now let's do that to all the other places. So let's right to do here. So we have a chart right here. So where is that located? Inside of our components chart right here.
So let's go ahead and do that in the chart. So I'm gonna add this, use paywall. Let's go ahead and change this feature to go all the way up. And let me just properly import this skeleton while we are here. Okay.
And what we're gonna do here now is if type is not area or whichever you want to allow users to look at. So I'm gonna keep the area as the free one. And then if I should block, I'm going to trigger the paywall and I will break this function. So this is important. Make sure you break the function.
Let's try it out. So I'm gonna go ahead and add, manually add a transaction today for example. Let me create a new account. Let me create a new category here. Let's go ahead and write an expense and let's create a transaction.
Let's go into the overview now and there we go. And if I try to change this to line chart, There we go. I cannot do it but I can see the area chart. Perfect. Now let's go ahead and let's do that in the spending pie, I believe.
There we go. So it's going to be the exact same thing. Go ahead and import use paywall. So let me just move that and separate the import for it. And let me align the proper import here for components skeleton.
And we're gonna do the exact same thing, right? But this time you choosing between pie radial or Radar I believe There we go. So let's try this out on this pie chart. There we go. And the last one is the settings here.
I should not be able to connect to my bank account if I don't have the subscription. So for that, let's go inside of the Play Connect. Let's go ahead and let's import our use paywall here. Let's go ahead and destructure everything we need here. Should block trigger paywall and is loading.
And now let's go ahead and extend this isDisabled we'll also check isLoading which we know comes from the paywall, right? So let's see if the paywall is ready let me just reload my window to get rid of the errors here And now what we're going to do is we're going to go inside of the on click here and if should block we are going to trigger the payroll and again break the method. And using this method you can paywall anything you want. Let's go ahead and try this out. Let's click connect.
There we go. And now let's go ahead and upgrade to see if this is really working. So Make sure you have your webhook running. I'm on a new account here. Let me go ahead and select Croatia.
Enter the postal code and pay. And let's finally see the result of this. Let's go back. There we go. Subscription active.
And I can now connect. I can now import. There we go. And I can now change my chart types. Amazing, amazing job.
So if you want to, you can go even further and you can use this same method, so this comparison and check that on the backend in some places as well. Great, great job. Thank you so much for watching the additional content and for supporting my work and see you in the next tutorial.