How to Monetize Your Flutter App: Ads, In-App Purchases, and Subscriptions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to the flutter app monetization complete guide course in this course you're going to see the various ways you can monetize your flutter app so the course will be broken down into a few sections the first section we're actually going to build a complete app out that we'll then use to monetize later if you already have an app that you want to monetize that's great you can definitely use your app and I would fully suggest doing that if you already do have an app but note that if you don't have anything built out yet that's no problem you're going to see how to do everything completely from the beginning of a brand new flutter app then we're going to talk about all the different ways you can monetize an app with ads so this is going to include banner ads interstitial ads reward video ads as well as native ads after that you'll see how to add all the different types of inapp purchases to your app and there are three main types the first is a consumable in purchase which you can think of as coins or something that a user will buy once and they'll use it and then it will be gone the second is a non-consumable which just means the user will buy this and I purchase once and then have it forever so the example that we'll be covering is removing ads from the app lastly we'll talk about subscriptions and how we can set up a monthly and yearly subscription to premium features in the app once we have the NAA purchases set up we're also going to build a whole backend server that's going to validate the Napp purchases this is something you're definitely going to want to build out if you want your app in production because it's going to prevent any users from Gaining access to your Ina purchases without actually having purchased them and this is especially useful with subscriptions because there are ways that users can and essentially trick your app into thinking that they bought the subscription at a certain date even though it may have expired so the backend server is definitely going to prevent all of that from happening and you'll see exactly how to build that out finally we'll get our app completely ready to deploy on both IOS and Android they'll both be ready in beta versions but you can completely deploy the app right after that the app that we're going to be building in this course is called the decider app and you can actually go ahead and download that right now on both IOS and Android by going to onean startup.com deider so definitely go check out the final version of the app and then let's get started building it out completely from scratch in this section we're going to build out a base app that we can then use in all the other sections to monetize so the app is going to be pretty simple but it's going to help the user decide on some decision so the user would ask the app some question like should I go for a run right now and the app will respond yes no maybe not right now give an answer kind of like that and some of the limits on this app are going to be the amount of questions that a user can ask so by default they'll have three questions they can ask and then once they ask all their questions they're going to have to wait 3 hours to get another question that they can ask so you can probably already see how this is going to allow us to monetize things we can sell the user a number of decisions we can let them watch an ad video to get some more decisions we can give them a subscription to an unlimited account where they can get unlimited decisions and all of this is going to be covered throughout the series but if you do want to see how to build out this base app then this section is going to cover exactly that this section is also going to be made completely free on YouTube and this is the only section of the course that will be made free on YouTube if you're unsure of the style and format of this course you can go check out those videos on YouTube for free at no risk and if you do decide that you want to monetize your app then you come back here and continue on through the rest of the course this section is also completely optional if you already have an app in codebase that you want to monetize then you can follow along the concepts in the later sections with monetizing and apply them directly to your app but do note that the code that is set up here is what we are going to be using for the rest of the course so it might be useful to watch through some of these videos or at minimum look through the complete code of that base app that this section is going to build out all right let's get started building out the base app in this video I'll show you how to set up a brand new flutter app using flutter 2.2.2 and since the setup is actually quite easy I'm also going to show you how to build out the base layout for the homepage of the decider app so let's get started all right so in Android Studio we have a new flutter project start here we're going to want to go with flutter application and then click next then we can name our project and we're going to be calling this the decider and the rest of this looks good to me then we can click next and you're also going to want to make sure the package name is a unique name I believe I might already be using the decider one on the produ version of this but I so I'm going to add the YT for YouTube right there keep both these boxes checked and then hit finish this is going to actually generate up the project for us here in Android Studio if you've been following along with the channel you might be surprised to see that we are now using dark mode yes that is the case we are going to be using dark mode for this entire new course first thing we should do here is just run the app so we're going to choose this iPhone 12 simulator over here and just hit hit run on this while that's loading I do want to show you the version of flutter we're using so if you just check flutter version like that you can see we're on flutter 2.2.2 which is the current stable channel of flutter and that is what we will be using for the entirety of this new decider course or until a new major version is released then we'll we'll update as needed but yes this is the version we will be using you can see the app runs as you would expect so all of that is looking good we now have our app created but we want to actually change this UI a bit in this video we're going to just go ahead and make the UI look like this none of these actions are actually going to work and this is all just going to be static data at this moment but let's go ahead and set up this page this homepage here and then in future videos we'll actually get everything wired up and working we currently we are in main. Dart we're not going to want to actually write all this in main. Dart we're going to want to break out some new files and actually create a whole new home View and put all that code in there for the home view let's go ahead and add four new packages here which are currently going to be empty but this is typically what I do at the beginning of a project we're going to have four new packages they're going to be the views package the services the models and extensions those will be like the four main categories of files that we're going to have another way to potentially keep this organized is to within this views package have a package for each of the views so for instance the home view could have a sub package within views and then you can have all of the files for the home view in there this app though is actually going to be pretty simple so I'm not going to do that I'm going to just create a new dart file and call it the home view so now within this we can create a state full widget and it will be the home View and then we will need to import material which if you do option enter on a Mac you can import material now this is just a container so nothing would really be showing if we had if we do have the app like this so really what we're going to use here is a scaffold so we can go ahead and add a scaffold here and then the scaffold is going to take an app bar and that's where we can give it the app bar with a title and that title is just going to be a text object and then we can call that the title of it decider and that is good for now if we go back to the main do Dart now when our app is currently running right now you can see it runs my app and then we go if we go into this class my app you can see it's a stateless widget and then we get this comment here that this is the root of our application we're just going to go ahead and delete that but it is true this is the root of our application you can see it is a material app here with the title of flutter demo we're going to want to change this title to be the decider and then you'll see we have some theme data here the primary color here we're going to change to red which is going to be the color of our decider app now the home here is you can see being passed to a my homepage with the title of flutter demo home page this we're going to change to the home View and the home view currently has no parameters that are needed so we just call home view like that now we do need to import the home view onto the main. dart so if you option enter you'll see the option to import the the package home view which is simply just going to import that up here if you save this now you'll see we do get that decider title up here which is being loaded from our home view now you'll notice we do still have all this code down here for the my home page which was being loaded earlier we can go ahead and just delete that cuz we are not going to need it anymore and same thing with the state date for that we are no longer going to need that so you can see our main do Dart is now much much smaller and we also have this much smaller home view one thing we can do real quickly is remove this debug flag which really doesn't serve any good purpose so you can do that by setting the debug show checked mode Banner to false and you'll see that now just went away all right so now let's go ahead and again just build out the home view to look like this so we can start with the app bar up here and just add these two icons we're not actually going to link them to anything yet but we can just place the icons there as a placeholder So within the app bar which is just representative of this whole red area up here we can have the title but then we can also have actions and actions take an array and then within that array we can give it any number of elements so the elements we're actually going to be using here are our gesture detector and that essentially is going to be like a button so then within here this has two parameters that it needs it's going to need the on Tapped which currently we're just going to leave blank and the second thing it needs is a child which is going to be what is displayed so for us we're going to use an icon you could also use text if you like but an icon is probably best here so we're going to use the icons of for the first one we are going to use a a shopping bag not sure this is actually the best icon for this but we will stick with it for now then we can copy all of that and just paste it again and since we're going to leave this blank as well there's nothing to change there but we will change the icon to the history icon so if we hot reload this you can see looks decent but it's not exactly what we want these are kind of squished over to the right here but this can be easily fixed with some padding so let's wrap this gesture detector in padding and set it to a actually only padding on the right so right and then let's increase it to about 20 now we will do the same thing below so just wrap this in padding as well and if we hot reload that you'll see our icons are now displaying essentially the same as our example app over here now we can move on to the body so we're going to just minimize appar there and start the body of the scaffold now because we're not going to have any sort of Bottom bar down here we do want to make sure we use the safe area here because text or anything could overflow down below this so all that to say start with a safe area here and that's pretty simple it doesn't really do anything other than prevent that overflow and now with the child of the safe area can just be a container and this container we really only need so that we can set the width of this and we're going to set the width to be the width of the screen we can do that with a media query and we just need to call of context and then give it the size and the width and now the child of this container is going to be a column and the reason we're going to use a column is because all of the information that we want in here is essentially is essentially just a bunch of stuff in a column so each of these elements can just be placed within our column and kind of listed out vertically so the column will take a children array and then this is where we can give our widgets within the column so the first thing is just the text that is going to give us the number of decisions so and we can just put a placeholder number there and it's actually decisions left is what we want this to say so if you save that you'll see it does pop up immediately we're going to quickly wrap this in some padding and it's really just going to be that default padding is enough just to bring it from the top there a little bit so the main part of this view is going to be this form here and so since the form is going to contain a little bit more complex code than just this smaller text with padding we're going to create this as a separate contained widget and then just call it from within this body this will keep the code a little bit simpler and easier to read so we will call this the build question form and down within the build State we can go ahead and Define this this will return a column because it does have three elements that we want to Stack Up return the column here and then for the children there will be those three elements the first one is just going to be the text and we do need to give this some style so the style here is just going to increase the font we use the theme headline 4 here and that will increase that font for us if we put a semicolon here it will remove that error as well if we save this you'll see we do get that down displaying up there in a larger font so that's good the second thing is going to be that actual text field and this text field will have a few additional parameters that we're going to want to add but for right now we can just do the decoration and set the input decoration and give it the helper text that reads enter a question if you save that you'll see the text field takes up the entire screen size so we do want to just wrap that in padding the final thing we need here is the button so we're going to use the elevated button class here the onpress for now we can just print clicked and the child here is is going to be a text value of ask the onpressed here is going to actually be a bit more of a function and if we format that everything should be lining up so if you save that we now have the button you'll notice though this is all shoved up to the top so we can fix that back up in our actual scaffold body build by adding some spacers so we add a spacer above and below the the build question form see it does Center it a bit we are going to add another text area down here which is going to then kind of push this back up so that final text area is essentially just going to give the account type which currently right now is going to be free and if you save that we we are looking essentially like how we would like if you did want to add a little bit more to the spacer on the bottom cuz if you look at this you'll see there's kind of a bit more space down here you can simply do that with the flex here and we'll set that to maybe three all right this is looking good our app is essentially laid out now how we need it to be laid out so the next steps we can do are connect this to Firebase and start getting this form actually working in this video I'm going to show you how to connect to Firebase so this is going to include using a new package Firebase core and linking your app to Firebase for IOS and Android let's get started so first we can add the Firebase core package by using flutter Pub add Firebase core and this should get us on Firebase core version 1.3.0 that is the newest version at the time of recording this once you have that you can see in your pope. emo that you will now have that Firebase core you can then run Pub get now we have the Firebase plug-in installed we now need to initialize Firebase so if we go back to the main. dart file we're going to need to add two lines above this the first one is going to be the widgets flutter binding we need to ensure those are initialized and then after that we can await the call to initialize Firebase which will look like that now we will need to add that package and we will also need to make our um our main a sync because we are now using a weight in here so that is good but we also need to configure Firebase with a new app in the actual Firebase dashboard so go ahead to console. firebase.com and create a new project this we will call the deci YT and then we can continue here we are going to enable Google analytics for this project select an account all right our project is ready we can hit continue here and it's going to bring us into our project so we are going to connect this for IOS and Android in this video I'm not going to cover how to do it in the web because this this app that I'm building currently is not going to be supported for web at this time so let's start with iOS and we need to get the bundle ID so if you go back to the project here and we can right click on iOS and then go flutter open an xcode once this opens an X code you can click on the runner so once that opens you can see the bundle identifier is right here so you could copy that and we'll paste that in right here the optional values here we will just leave blank so then you can click register and now you're going to want to download this Google service info.plist and we do need to add that into xcode here within xcode we're going to add that file right into this Runner folder here so you can just drag that in and keep the these items checked and then click finish I must have already had a download this so it got a one at the end of it so make sure the name is just this that is good so we can go back into Android Studio here and now if you go in the iOS file here and go to Runner you'll see we do actually have that same that same Google service info pist file there so you could have added it there as well the last thing we're going to do is open up this info. pist file and scroll down to the bottom we need to add this copied code here right at the [Music] bottom and this code I will link down below where you can get the copy of this this is all based on the flutter fire documentation so if you save that and then go back into here and click next we basically just did all of this for flutter so you can hit next there again and you can hit next as well and now you can continue to the console so our IOS app should be configured and connected so we will want to rerun the IOS app which it is going to use that Firebase initialization up here which if all is working correctly it will connect to Firebase here but while that's running we can actually just go ahead and do the Android one so a similar thing we need the Android package name this we can go and get an Android Studio if we go under Android app and then the build build. gradal file and if we scroll down a bit we we'll see that the application ID is right here and we do want to specify our own unique one so add the YT to the end of that we can remove this comment here while we're in here we're going to bump this Min SDK version up to 21 and copy this over and then go ahead and paste it here the rest of this we can just leave blank for now this file we will also need to download and back in Android Studio we're going to add that directly to the Android app folder that we were just in so go ahead and drag that on over there and then we can click refactor and add since we bumped that minimum version we do need to change the Gradle exception here to a file not found exception and once we do that that error should go away now our IOS app should be running all right so we are having some issues running the iOS version so these are actually being caused by the deploy Target being version iOS version 9.0 so we're going to bump that up to 10.2 so this can be done in three quick steps so firstly we need to go to the iOS folder over here and then go to flutter and app Frameworks info.plist and you'll see the minimum I the minimum OS version is at 8 here so let's change that to 10.2 then we're going to actually open up the Pod file itself and uncomment this line at the top and change this as well to 10.2 and then finally a bit further down here in this post install we're going to add this line here and also change this to 10.2 once all that is done we're going to open up the terminal and run a flutter clean then we're actually going to remove the Pod file. loock and pubp do loock and actually we don't have we don't currently have a pod file. loock so it must have been removed in the previous step then we're going to remove the pods folder and the runner works space and then we're finally going to rerun the app all right and we do have one small error and it is in this info.plist file I pasted this in the wrong location here we need to have that with inside this dictionary here so we can save that and try running it again now all right great you can see the IOS app is now running so we can go back to our Android setup now so we're going to go back over here here into our Android folder and we want to find the build.gradle file now this is a different build.gradle file than we were just using this the one we were using earlier was the one within the app directory but now we are just using the one within the Android directory so we want to look at the build scripts here and find the dependencies and we want to add a new dependency here and this is going to be for the Google services and we can just save that now we want to go into that other build.gradle file which is in the app directory and we we want to apply this plugin right here where we're applying these other plugins we can apply this plugin which is going to be the Google services plugin which essentially is going to be using that Json file from Firebase while we're also in here we do want to do one more thing and enable multi decks and this is going to prevent an issue that actually occurs when you try and run the Android a flut app on Android all right so now at this point we can try and run our app on our Android device if we just go back into Firebase console here we can hit next through all of this stuff because essentially we just set it all up now you'll see we do have the two apps configured here both should be working correctly so let's just make sure that Android app runs all right and everything looks good on Android so at this point we are configured with Firebase but really nothing substantial has been done yet but Firebase core is now configured with our project so in the next video we are going to add authentication with Firebase and and then after that we are going to be adding actual Cloud fire storage to start saving information that's entered into this question field in this video I'm going to show you how to set up Firebase authentication and then also how to automatically authenticate a user using Anonymous login as soon as the user enters the app so the reason we're going to do this is because we don't actually want our users to log in or at least we don't want that feature yet so we're going to immediately log the user in just so we can get a user ID and then we'll later use that user ID to keep track of all that user's specific data but the user won't even know that they're getting logged in or having an account created for them it'll just feel like they're just using the app so let's get started so the first thing we're going to do is open up our pub. yaml and we need to add the Firebase off dependency and then we'll run Pub get then we can go over to Firebase itself and we need to actually enable authentication So within our project here we can click get started on the authentication and the only one we're going to enable currently is the anonymous authentication so we can hit save there so now Anonymous authentication is enabled and you can go over to users here you'll see we have no users which is expected currently so back in Android Studio here we are going to go into the services package here that is currently empty and we're going to add a new dart file and we're going to just call this the O service so this is going to be a custom class called off service so the main thing we're interested in doing here is creating a function that can check to see if there is a current user logged in so our app right now has no current user logged in and if that's the case and there's no current user then we want to create an anonymous user and and log that user in without the user even knowing and then next time the user comes to the app they're they will be logged in so it should find that user ID and not create a new user again this is all just so we can track a user's data through Firebase so we just need to get them that unique user ID so this is actually relatively simple we need to create an instance of Firebase [Music] off and we'll just call this Firebase off this will just be a Firebase off instance so now we have our instance we can create a quick getter that is just going to get us the current user and this is complaining here because we actually have no safety turned on so potentially a current user could be null here so to change that we just need to make this return type knowable so a current user could be null and we do want that cuz as I said the app state right now the current user would be null so now we're going to create that function where we're going to get or create a new Anonymous user and this is going to be a future that is going to be returning a user and it will always return a user because we're either going to get the current user or create user so we'll name this get or create user [Music] and now we can do a quick check on this current user getter up here and we can just say if the current user is null then we're going to actually create the user so we're going to await a call to Firebase off and we're going to Simply call sign in anonymously now that we'll be signed in we can actually just return we can actually just return that current user and again we're going to have to make this return type nullable because in theory this could be null but in actuality it never should be because really what we're doing here is checking if it's null if it is null we're going to create a user which in that case the user will be created so when we come down here to make this call again to current user at that point it will get that new user that was just created now this get or create user function here needs to be called as soon as the app loads so we only want to call it once and we want to call it when the app is first initialized so it's going to happen over in Main and what we're going to want to do here is right after we initialize the app with Firebase we're going to make another call to our off service and we're simply going to call that get or create user here and that will log in the user and get us that current user back this should be working if we restart the app and you do have to restart the app so that this gets hit then we should go we should be able to go into Firebase here and see that we have a user account now so yes you can see we do have one user account that was created and that would be the user that is using this iPhone app so if we restart the app here it should not create a second user because it is going to be pulling that user ID that is already signed in one nice thing to that I've do during testing is actually just print out the user ID that makes it a little bit easier when we're dealing with these Anonymous users because it can get hard to track the data when you're going between Firebase and this so this can be done simply by adding a text here that is going to create an instance of our off service and then it's going to call the current user and since that potentially could be null we need to null Escape that there and then call the U ID on that and at that point you can see we do get that that user ID down here if we were to go ahead and run this in our Android app you'll see as soon as the app loads we're going to have a separate user ID there and that is just another way to confirm that this is working and not using the same user ID for across all instances of the app but it is using the same user ID for the same device all right yes you can see here we do get a different user ID on the Android device which is expected and then if you check over in Firebase as well we now have those two users in this video we're going to set up the quote unquote algorithm for how we're going to answer the questions that people are asking so again the way that this app is going to be set up is a person is going to be asking the app for a decision so it'll be framed in a should I dot dot dot and they'll fill in the blanks so for instance should I go for a run and then the app will respond to the user with some sort of answer so it'll be maybe yes no definitely maybe it'll be some sort of answer like that so this video we're going to set up how the app is coming up with those actual answers let's get started all right so when someone enters a question here and they click ask we want to get a random value for the answer there so there are a few potential ways we can do this but what we're going to do is have a list of potential answers and then randomly select one of those answers as the app develops we might make this a bit more sophisticated and actually add a little bit of predictive answering there but for the point of this app in this video I'm just going to show you how you can have a button click generate a random value this is actually relatively straightforward but we have our button down here and the onpress right now it just says clicked so on this onpress we're actually going to make a call to get answer and this is a function that we have not yet set up so right below the build here we can go ahead and set this up and this is actually just going to return a string value and we want to define a list here so these are going to be the answer options and it's going to just be a list of potential options so we're we're going to go with these four options here which are yes no definitely and not right now you can to add more options if you like or just leave it simple like this one and now we just want to return a random value in this list here so we can do that with taking the answer options here and getting a index that is between 0o and three three because there currently are four values in here so we just want a random number between that so we're going to call random and then call next int on this and we need to get essentially for this case we would pass in four because it's the length of the array here but we don't want to just limit it to these four values if we want to add another one later we want this to work without having to update the length down here so we can simply use the length of the list here now random is going to need to be import using Dart math after this now we should be getting a random value of this array so let's go ahead and print that just so we could see it in the console and then if that's looking good we can go ahead and start displaying it so we did forget a semicolon here but if we reload this and we hit ask you'll see we get yes no not right now no definitely yes you can see these are random there is no they're not in order at all so this is what we want and this is essentially the backbone of the app's quote unquote algorithm so that is the simple algorithm we're going to use to determine what the answer answer is now we can add in the answer below this when we click the ask button and then later we'll add the question as well so in this build question form here below the button we can call a new widget which will create here and it will be the question and the answer and we can Define this widget right down here so we want this to display the question and the answer but since we don't really have the question set up now we can just have it displayed the answer for now so this should return a column with two text values and the first one is going to be the question which is going to be in the form of should I and then it will have the question that was entered because we aren't going to assume that they're going to type should I since should I is already there and then below this we will have another text value and this will be the answer now for the answer here we're going to actually Define a new variable and we're going to call that the answer so this answer is going to need to be defined up here and it will be a string value so we can just Define that as an empty string now when we click the button we can just set the state of this answer to be the value that we were printing out so instead of printing out the answer here we can instead use set State and set the answer variable to the value returned from get answer now we have the text of the answer down here it should be showing that answer value every time it changes so if we save it you can see it does say should I since I just reloaded it there is no answer variable saved yet as answer it's just an empty string right now but as soon as I hit the ask button button it now becomes one of those random options in the answer if you click this again you can see it will keep changing and since it is random sometimes it looks like you click it and nothing happens but that's just because that value is being repeated in this video I'm going to show you how you can extend a class in flutter so in the example we're going to extend the string class and add a new function to the string class called C capitalize this will be useful because you'll be able to use this new function on any string throughout your app and all you have to do is call capitalize on any string in your app to use it all right let's get started so currently when you click the ask button here we get a value back that is one of these values in the list here and it is randomly generated firstly let's change the text here to be styled a little bit differently and we're going to first add answer to the beginning here and then escape this [Music] out then we'll add a bit of style and we're just really going to change the text size here to be slightly bigger so if you save that you can see the size is just a little bit bigger I also going to quickly add some padding all right so I want this value here to be capitalized and obviously the simple way to do this is to just go ahead and change those values all in here to be capitalized but I think this is also a useful extension to make in a flutter app because sometimes you do want to capitalize things that maybe are coming from the database or you don't have that exact control to change it just in the text like like we can do right here so anyway we're going to add a new extension and I did create this extensions package initially which is currently empty but we're going to add a new dart file here and this is just going to be a string extension and within this string extension we're going to Define an extension it's going to be called the string [Music] extension and it's going to be called on the string class so we're basically taking the string class that is used in flutter and we're going to extend it and add a new function to it so this function is going to capitalize a string so so we're going to call it capitalize and now we can return the logic that would go into capitalizing a string so if we have a string value here we're going to be able to take the value Itself by calling this so this here is just going to be referring to the string that we're calling capitalize on so this will be called when we use the capitalize method it will be called with some string like this and then we can call do capitalize so this is how this is essentially how it will look when we call this as an example and this variable is actually going to be referring to this string here so we can take the the string that's passed in and get the first letter in it so using I or using zero there is essentially just going to get us the first first letter in the index there and then we can call two uppercase and then after that we can take the string again so that this value and create a substring on it and with the substring you just give it the starting position so we're going to start at position one because we want to skip the first in we want to skip the first value in the string because it's already going to be added right here as the capitalized letter and then basically we're going to take the rest of the string as a substring and join that to that capitalized first letter and this is also going to be two lowercase we can call on it although it will most likely already be lowercase but this will by calling to lowercase here and actually that needs to be called within that br bracket by calling two lowercase here that is also going to make this capitalized function work for something like for some type of string like this if it was a if it was a string like this and we called capitalize on it then it would actually make all of these lowercase and keep that first one uppercase so it makes it a little bit more useful in my opinion um but that's it for that I'll remove those examples and now we can call capitalize on the answer here and just call capitalize and we will need to also just import that package so if you save that you can see now the value is capitalized and if you click through they all are capitalized and just the first letter is capitalized there which is what we want and as I said before if we were to add another value here or if we were to make this all capitalized you can see even if we go through true it just came up but the yes value is is as we would expect it is capitalized this is kind of a very simple example of how you can extend a class and flutter but it is it is nice and illustrating basically any class that you want to extend you can add new methods to and then use this as a reference back to that class that you are extending on one thing to note if you completely rerun the app here you're going to see an ER now about an invalid value basically that the index is out of range and the reason for this is because our answer variable right here is actually just an empty string so when we call capitalize on it it's going to try and get that first value and make it an uppercase value so there's a few ways we can fix this we could fix it right within here and just return the string if the if the value is an empty string so if this so if this equals an empty string then we're just going to return this and then if it's not then we'll return the capitalized version of it you can see now the error went away but we do actually get this showing still when we don't have an answer so we can also add a quick if statement here and we can check if the answer is equal to an empty string and if that is the case then we're not going to set this so actually let's use not equal here and we will return this if the answer is not equal to an empty string which means it's actually going to have an answer and if it is equal to an empty string then we're just going to return a [Music] container so now that whole block goes away but if we click this now that we have an answer it will show in this video I'm going to show you how to add some more features to your text field so these are going to include hiding the keyboard when you touch outside the text Fields disabling the button until the text Fields has some content in it making the text Fields have multi-lines of text and then also adding a controller to the form field so we can extract the text that's entered and use it elsewhere in the app all right let's get started so currently when we hit the ask button here we get our answer printed now but we don't actually have a way to get the question here this video we're going to cover how to enter a question and then also reuse that question down below the first step to this is going to be updating our form field here so find where we Define that which is in the build questions form widget and you can see we have our text field right here we're going to add a few new properties to the this text field firstly we're going to set the max lines to null and what this is going to do is allow for multiple lines in the text field next we're going to set the keyboard type to be an input an input type of multi-line and the reason we're doing this is so someone can enter a bit of a longer question here it'll just allow them to type more than just the one line and it'll actually go down to the next line now we can add our controller here and we're going to just call this the question controller and now we're going to need to Define this controller so up at the top of our home view State here we can Define the controller and it'll be a text editing controller this controller is what's going to allow us to get the value of the text field and display it down below after the button is clicked so right now in its simplest form we can take this controller and down below where we're calling question and answer which is this block of text down here you can see it says should I and then we just have this placeholder of these numbers of these number signs here so instead we can go ahead and use that controller and we can call the text value on it now if you save this it won't quite have anything there but as you type a question in and hit ask it will now populate that down below with the question and the new answer there is one more quick thing that we're going to want to do with this text field here and it's really actually more noticeable when you use the actual keyboard so if you use the keyboard here because we made this a multi-line text the default here is going to be a return button which is going to allow you to return to the next line but really we only want that to happen if someone is typing out something that's longer we don't really want them to be adding ENT in themselves so we would prefer this to say done because right now there's no way to actually close out this keyboard so we're going to do two things we're going to add we're going to set this button to be the done button to hide the keyboard and we're also going to make it so you can click anywhere here to hide the keyboard to change this return button to a done button is actually pretty easy all we need to set is the text input action and we're going to set it to the text input action of done so if you reload that you'll see now this is the done button and we can hide the keyboard we can also get this keyboard to hide by clicking anywhere outside of the text Fields by actually adding a gesture detector to this whole page so we're going to do that by going up to the body here and we're going to wrap this scaffold in a new widget and the widget we're going to be wrapping it in is a gesture detector and then this gesture detector takes an on Tapped method as well as the child and the on Tapped here we're just going to set as a oneliner the focus manager instance and then we're going to be looking at the primary focus because currently this form field here is focused so when this is focused and we're going to check because it might not be focused but if it is focused then we're going to just call unfocus on that and if you save this now you can see if you type if you click on the form field it's focused now we have the gesture detector everywhere else on the page so if you click anywhere outside of the form field it will unfocus that field and go ahead and hide the keyboard for you I think both I think having both of these is nice because some people might want to click the done button but it's probably more natural to actually just click outside but that might come down to personal preference the last thing we're going to do in this video is set this ask button to be disabled until there's actually text in the form field So currently if you click ask it will print out this stuff down here which doesn't really make sense because there was no actual question asked and later when we're actually going to be counting people's questions we want to make sure they're not wasting a question or a decision rather on a blank question so we're going to do this by creating a new variable and this is just going to be a and we'll just call this ask button or BTN active and by default we will set this to false because when the page initially loads we won't have any text in this form field so now that we have that variable defined we want to go ahead and set that to true if there is any text so within our text field here we can use the on changed function and this will take a value here and that value will be the value of the text field and then we can just call set state anytime this value is changed and we can set the that new variable that we just defined to equal true if and this is going to be a bit of a shorthand here so if the value which is the value of the text field if the length of it is greater than or equal to one if this condition here is true this is the shorthand if then we want to set this variable here to true and if not then we want to set it to false so now this should be getting set to True when the value here has a value the last thing we need to do is actually disable this button if the value is false so we're going to actually extract this this set State here into a new function and we'll put this all the way down at the bottom and these are going to be our void functions so this is going to be a void function here so the void functions here are going to be the functions that do some action but aren't actually returning any widgets so let's call this new function answer question because it will be what is answering the question and for now we're going to Simply copy this set state that we had in the in the button click and put it right here so now we can just replace this set state with the function answer question and if you save it nothing really changes at this point but what we can do now is also write another shorthand if on this onpressed here and we can actually make this onpressed a oneliner so we can check if that ask button active is equal to true and if that condition is true then we are going to actually call this function here and if it's not true then we'll just then we'll just use null and and what using null in this case will do is deactivate the button we will need a comma after this instead of a semicolon because of the way we wrote this as a oneliner but if you save this now you'll see the button is now inactive and then as soon as we click in here and type one character the button becomes active if you want to add different validation here for instance you want want the question to be three characters long or maybe you're working on a form field where you need some other longer string you can see this now doesn't work until it's three characters long so now our form is set up and validating the ask button and essentially working how we're going to want it the next thing we can do is actually start taking this information and modeling it into a question answer object and actually saving that up to Firebase in this video we're going to connect to Cloud fir store and we're going to use cloud fir store to store our users data about their questions so anytime user asks a question we're going to store that in fir store and the reason we're going to do this is because we want to also have a list of past questions so user can go back and see their past questions so to do that we are going to need to store those questions somewhere and by the end of this video you'll actually be able to ask the question in the app and it will send to Firebase and save it let's get started so we're going to start by adding the cloud firestore package in pspec doyl and we're going to be using version 2.2.2 which is the current version as of time of recording this video once that package is installed we also need to go over into the Firebase console and click on fire store database and we do just need to create a datab datase here so we're going to start in test mode which is going to allow read and write of anything for up to this timestamp here you will want to add security rules before you put this on production but depending on how your app is set up the security rules will vary and I do have a video on the channel describing how to set up the security rules if you wanted to check that out now it's just asking you for a location so just choose the location that is closest to you and click enable once that loads up you'll see we now have an empty Cloud firestore database and since we added that package to flutter we can actually start adding data to the database the way we're going to structure the data is all going to be based on the user's ID so in the past videos we set up this Anonymous account when a user opens up the app for the first time so we do have a user ID down here if you did set up your app with different types of authentication but you are using Firebase authentication you'll still have this user ID same way even if it's a login with Google or login with email and password you'll still have that unique user ID so with our database setup we're going to have a users collection and then we're going to have a document with the name being this user ID and then within that we are going to have another collection of all the questions and we'll also later be able to put some more profile data on that users document directly So currently right now when you enter a question here and click the ask button we're going to be getting the answer and setting the state of that answer and that's going to happen in this answer question void function down here and you can see where this is called it is just called on the on pressed of the button so we're going to be putting the logic within this void function of actually saving that question and answer to Firebase so I'll put a comment here and just this will be where we save to to a database but we want to actually model out this data first so we're going to create a new model for a question and it's going to contain the question the answer and the time of this question being asked so in our models directory over here we're going to add a new file and it will be a dart file and this is going to be called question and now we'll create a class called question and model out that data that we want to set to Firebase so we're going to have those three values the question the answer and we're going to also have the date time which is going to be created at or we'll just call it created and now we need to initialize the question and we're not going to require any of these three parameters to to actually exist so we can just initialize the question by calling question here and since we have null safety on we are going to actually have to add a question mark to the end of these types here because we are going to allow these to be null now when we upload a question to Firebase we need it in the format of a map which is going to essentially just be the key value pair that we want to upload to Firebase so we can create that function real quick right now and this is going to be returning a map which is going to have a string key and the value will be dynamic and the reason for that is because these types up here are all different and we're just going to call this to Json and then all we need to do here is actually just set each one of these parameters as a string so we're going to have the question and that is going going to map to the question then we'll have the answer which maps to the answer and finally we'll have the created which maps to the created I'm actually going to change question here to query and that looks good so now we basically will be be able to call 2 Json on any question object and all it's going to do is return the data formatted like this which is the exact format that we need to send to Firebase so now that that's set up we can go back to our home View and use this question to create a question object that will be used to send to Firebase once this ask button is clicked at the very top of this state here we are going to want to define a question and we will just call it the question and then initialize a question here and now we can use this question object and set its query and answer values based on the input and the answer that we're generating so down in this answer question here we can update this question query and it's going to actually equal the questions controller text so if you find where we're using this questions controller text which is right up here where we're displaying it can actually go ahead and also replace that with the question objects query and then we can also update the questions answer here and the answer is simply going to equal the answer that we are already setting up there and lastly we can set the question create and that's going to just equal the current date time which we can call with date time. now so now our question object is going to have the three parameters set on it so we can now use that object and actually save to the database I'm just going to delete that comment for now so the first thing we need is a Firebase instance and this is going to be an asynchronous call so we will need to await it which also means we need to make this an AS synchronous function so we're looking for that Firebase fire store instance and once we have that we can call collection on it and we're going to be creating this new collection called users and then we want to create a document on the users collection with that user ID so if you look how we are displaying that user ID right up here we're just using our off service and calling the current users U ID so we can just drop that right in here and I'm going to break this into a few lines here so it's a little bit easier to read but after that document we're going to create another collection and this collection is now going to be for the questions so we'll call it questions and then finally we're going to add that question object to this new collection so it is going to be that question object that we created and we already set all the parameters for it there and now we will call to Json on that so the reason we're calling this two Json oh it looks like I lost the question variable there so the reason we're calling that to Json on it again is just so that we can get the question formatted like this if [Music] you aren't fully understanding this basically what this is doing it would be the same thing as if we wrote it it would be the same thing as if we wrote this right here and if we replaced query here with actually this part this text you know if we changed it like this it would achieve the same thing but having it as an object makes it a little bit easier in my opinion to read and easier to work with because we can always just grab that question parameter whenever we need it but now that this is set up if we rerun the app and we try asking a question should I go to [Music] Firebase and the answer is yes so that's a good sign although as you know this is just randomized so let's see if it actually worked and went to Firebase and it looks like it did not all right so that didn't work but I actually restarted my Android Studio sometimes when you add a new package like we did with this for cloud fire store there's sometimes issues that I've noticed with Android Studio actually compiling that correctly so sometimes I need to completely shut it down and restart it so let's test out and see if this works now and if we come over to Firebase you'll see it did in fact work so it was just an issue with with my Android Studio so if you have the same thing consider restarting Android Studio but you can see the question is here and this is the document ID it's actually just a random string here which then has all three fields which we added in so this is working great one thing that you might notice is the question is still displayed up here so we can quickly remove that just by setting the question controller text equal to an empty string so after this call is made we're just going to set this equal to an empty string and that is good another thing you might notice is there's kind of a space after this question mark so if you come up here and just remove that space that should fix that now if we rerun and type in a new question and hit ask you'll see it does clear us out there and this is now formatted correctly and our new question is also going to be right here in fir store in this video we're going to set up the list of past questions and we're simply going to do this by listing out all the questions and answers in the date that they were asked for each user so let's get started in the last video we set up Cloud fire store 2 save our questions and answers and in this video we're going to display the history of questions and answers in a list view which is going to be a completely different page which will be accessible by this icon up here so let's first start and create that new view which we're going to name the history View and this is going to be a state full widget and of course we will need to import material for the view State here instead of returning a container we're going to return a scaffold and within the scaffold we'll set the app Bar's title to be the text of past decisions then for the body which we are going to want to use a safe area we can for now just keep a container for the child now let's link from the homepage to our history view so we can see that this is working correctly so back in the homepage you're going to want to find the actions where we have our action for the icon of history and right now the on Tapped is simply blank so we're just going to add a call to navigator push and we'll push that material page route and the Builder here is going to be the history View and we will need to give this the context here so if you refresh your app and click on the history view icon you can see it says past decisions as we would expect and you can go back and forth between this page pretty simple stuff here but that is linked up now so back in the history view we need to actually get the data from fir store to create this list here so the first thing we can do is Define a list in our history view State and it will be a list of we can just do a generic object list right now and we'll call this the history list now that we have this history list we can create a function that is actually going to fill it with some data from fir store so let's create a function down here and it's actually going to be returning a future since we are going to be getting that data from Firebase and we'll call this get users questions list I suppose so right here we're going to want to make that call to Firebase make sure that this is asynchronous as well and now we can Define the users uid which again I've discussed in past videos which is being displayed right down here which is just the Firebase off user ID and if you remember correctly the database is set up to have this user collection and then the uid is the document and then this document contains this specific user's all their data so anyway we need to get that uid right here so we can Define that as a final and right now we can get that uid by calling our off service and then we can just call current user and uid and remember that this is null safety so we do need to make sure that that question mark is there because potentially the current user could be null now that we have the uid we can actually make the call to get all of this user's questions and we'll set that as a variable called data and that's going to be the asynchronous call to Firebase so we need to get a Firebase instance and then we'll get our collection which is the users collection and then we'll look for the document of that user's [Music] ID and then within that there's going to be the collection for the questions and it's called questions and with these questions we want to order them by the date that they were asked so currently they're just going to be ordered randomly by I believe it's just alphabetical by the ID of the document but since this is random these are going to kind of get out of order so we can add a order Clause here to order them by the date and the field we want to use is going to be this created field and we're going to want to make that descending true so that the most recent one is at the top of the list and after calling the order there all we need to do is call get and this will get us the snapshot of that data so we're going to have that data as this data variable here and once we have that we can go ahead and set the state of our history list and we're going to set that equal to a list here and it's going to be a list and we're going to cast it from this data and we're going to take this data which is a a query snapshot and we want to get the documents from this query snapshot so we can call docs on it and then once we do that we're going to actually map each one of these documents into an actual question object so we don't actually quite have that set up in our question object yet so if you go into the models the question right here this is what we ultimately want the data to be formatted as so we're going to pull each of these two questions from Firebase and create a question object with that data you can add a quick helper function here that is going to convert the query snapshot data to an actual question object similar to what we're doing here it's just going to be the reverse order so this one we're going to be creating a question from a query snapshot so we're going to call this from snapshot this is going to take a parameter which will be that snapshot so this will represent the document from Firebase and this is a bit of a shorthand way to write it but we're going to justif find each of these variables now so the query is going to equal the snapshot's data query parameter so if we call data here and then the data will be essentially looking like this now and we're going to want that query parameter there we're going to do a similar thing for the answer and lastly the created since the created is a date time we do need to actually convert that to a date object here so don't forget to do that or else this will error but now that we have this from snapshot we can create a question from a snapshot by passing it a document snapshot so if we go back into our history view right here we have all these documents so we want to go through each of the documents that we have and in this case we currently have two and we want to create a question for each of those documents so the quickest way to do this is actually to use map and map is going to go through each of items in this that we're calling map on so it's going to go through each of the documents and allow us to perform a function on each of those documents so each of those documents will be represented by this doc here and you can name that whatever you want it's kind of just a placeholder variable and then you can say what you want to perform on each of those documents so we want to create the question from the snapshot and then we're going to need to pass it that snapshot so it's going to be that document there the question is going to need to be imported that package but once that's done you can add that semicolon and now we should have our history list as a list of questions so the last thing to do now is actually to display this list in a list view Builder so up here where we have the this container we're going to replace this now with a list view Builder and the item count of this is going to be just the count of our history list so we can just call length on that and then we're going to have our item Builder and this will have the context here as well as an index so the Builder we're going to actually want to build out a card that has the question and the answer but for right now just to make sure that our list is populating with two values let's just return the index here as a text value and then if this once this is working we will build out that card so if you save this and look there's actually nothing loaded and that's because we're never calling this get user question list which means we're never setting the history list to anything so we're going to set this in the did change dependencies override so right above the build method we can override the did change dependencies and simply call this get user question list since we're calling this in did change dependencies it is going to unwrap that future essentially and once the future returns it's going to set the state of this history list so this is kind of a nice way to get data that is return from a future without having to use a future Builder but if you save this now and we will need to go back to the main page and then enter here you'll see we now have 01 which is expected because we have two items in our list here that are being displayed so now we need to build the card actually that we're going to display this data in and we can create that actually as a new file so in our views let's create a new package and we're just going to call this the helpers and now we'll create a new dart file and this will be the question card this one will be a state list widget and it's going to be called the question card and we will have to import material there now this stateless widget for the question is going to take a parameter which is going to be a question and we'll just call this the question we will need to import the question package as well so that we have access to the question model now we need to create a quick initializer for this so question card is going to be initialized with a question and now within the build method here we'll keep this container and the child element of it we will make a card so within this card we want to just display the question and the answer in the date it was asked let me actually write this all out and then I'll just go over it because I think it is kind of pretty basic so no need to type it all out here all right so this is all we need we have a column with two rows this first row has a little bit of padding and then it just has should I with the question and we'll actually add a question mark there as well this is essentially what we're doing on the homepage when we're asking the question down in this area and then we have another row which is going to give the answer to the question as text and it's going to be formatted a little bit Bolder so it'll be a little bit of a bolder font there again that's pretty similar to what we're doing on the homepage for the question that's asked down below lastly we're going to have a bit of space and then we're going to have the date that the question was asked and we're formatting the date here with this date formatter and all you have to do to use this is to pass in the date that you want formatted which is the created date from our question object another thing to know is we are making use of that capitalize extension that we created a few videos back so that is nice as well but now we can use this question card here in our history view list Builder so back in the history view instead of returning this text here we can now return a question card and the question card is going to need to be import and then the question card does have that one parameter which is a question so we can get that question by getting the history list index which since the history list is all questions we can pass that index in and then we're just going to need to type cast this as a question I believe and if we save this you'll see we now have a simple list view here where we get the question and we get the answer if we go back and ask a new question which can be just anything and hit ask it's going to answer a question there put it in Firebase and if we go to our history view you'll see we do get that test question right up at the top in this video we're going to configure provider so that we can have our user authenticated in main. Dart and then basically be able to use that throughout the app that's just going to be one example we're going to use provider for for multiple areas in the app as well but this video is just going to be covering how to actually set up provider so to begin we're going to open up the terminal and type flutter Pub add provider this is going to add the provider package for us and then we can go to pubp do yaml and you will see we now have provider 5.0.0 and let's just run a pub get after doing that so now we should have provid and the configuration for this is actually going to happen in our main. Dart so let's close out all these files and open up our main do Dart here and right here within our main call we have the run app and we're going to modify this run app to actually be wrapped in a multi-provider the reason we're going to use a multi provider is so that we can have multiple different providers in this example I'm only going to be setting up one which is going to be for the off service we created but in the future we're going to actually use this provider for our admob service so it's good to just use the multi-provider in my opinion when you start out because most likely you will want multiple different providers so in run app here instead of calling my app directly we're going to comment that app for now and we'll call multi-provider and multi-provider takes a list of providers so we can create a new list here and pass it any list of providers so we're going to give it a provider with the value of the off service and this off service was created in a past video which I'll also link here but if you go into the services you can see real quick it's pretty simple but it does give us access to that current user so with this provider we'll have access to the off service as a whole after that that we're going to define the child element here and that's where we are going to place my app we can remove that comment now actually going to change my app here to be called the decider app and we can do that with a quick refactor and rename and then we're going to call this the decider app that's not 100% necessary to do but I prefer the name to actually match the name of the app we're working on so now that the provider is set up we can actually begin to use it so currently on the main. dart there's actually no calls to the off service but if we go into our home View and search for the O service you can see we're creating a new instance of this o service and then getting the current user so this is okay and within the context of this app in this example this is actually a pretty simple example so it's not necessarily something you need provider for but it will demonstrate how you can use it so instead of calling off service like this we can instead call it through the provider and we can do this by calling the context which is going to be aware of the entire widget tree and take the context and then call read on it and we're going to be wanting to read that off service so this is actually the provider that we have and then we would call current user uid so to use the read context we are going to have to import the provider package so be sure to add that up at the top but if you rerun the app you can see we do get that current user through the provider now so the benefit here is that we aren't creating a new instance of off service it is going to be that same instance of off service this is useful especially if the off service was more complex anyway we can use this now and and replace the other instances of our off service so we're using it down here and we can go ahead and update that and only those two instances were being used we also have one instance in the history view where we use the off service I to get the current users's ID and actually this U ID variable here isn't even quite needed we can kind of just move all of this into where we're calling that and just remove that variable completely we will need to import provider as well in our history view so we can copy that package over into there so if you rerun the app everything should work as it did but now we have provider set up and we'll be able to use that for this off service and get an instance of this off service anywhere in our app as well as now we're set up with provider so we can start adding new providers which will become very useful later when we actually set up admob integration in this video we're going to set up the user account and we're going to create an actual account object so that we can start storing information about the user some of that information is going to include their total Bank of decisions so how many decisions they actually have left then we're also going to store when they're going to get their next free decision so on the free plan we are going to have a 3-hour delay between the next free decision we'll set up that time and hold it on their account we can also use this account to store other things like if they're a premium user or if they have the unlimited subscription so let's get started setting up the accounts so you can see in our demo app here we have no actual value here this is a value we're going to want to save in Firebase and then be able to update based on questions asked or later in a purchases purchased can and increase that number so to start we're actually going to create a new model and this is going to be called the account model and this is going to be called the account and we'll just create this class and this is going to have a few parameters firstly we're going to have the string parameter for the uid then we'll have an INT which is going to be the bank is what we'll call it and the bank is going to represent the amount of decisions a person has in their quote unquote Bank their decision bank and we're not going to require either of these so we will add a question mark there and then we will call the account on this to initialize it which essentially that is it similar to what we built in the question model we have the to Json and from snapshot functions which we use to both update and get data from Firebase we'll do a similar thing here obviously we'll just change this to bank and the uid is actually not going to be updated explicitly but we will use this property to store the user's ID on their account that will just make it easier so we don't need to keep calling current user we can actually just link it to the account object that we're going to be using and then just get their user ID from that uh let's modify some of this as well from snapshot and we'll just update the bank here so so this is set for now and we should be able to update the bank and also get it from the snapshot so now we have the account so this account model is a little bit different than how our question model is set up in terms of where it's getting its data from Firebase so if we look in Firebase here we have our users collection and then we have our individual user here and for the questions model we are putting each individual question as a document in the collection for questions and you can see the the fields within this document map directly to the fields that we have in our question model now our account one is going to be a little bit different in that it's really just going to be the fields that are on our users document so we have our user document right here and the account fields are actually going to go right here and be directly on this document for that user's account really the naming of it calling an account makes sense within the code but all the data is going to be stored right here on this document so now that we have our account model here we want to actually set the user account to have a default Bank value of three when that user is created so we actually can do this over in our service off service and we're going to create a new function here called initialize account and the first thing we're going to do here is get that document which is going to be actually a type document reference and for this we will need to include Firebase fir store so as I was saying before this document here of the user is the one that we're going to get here so we're going to need a Firebase fir store instance and then we can call collection of users and from that we are going to get the document which is going to be the current user's uid we will need to remember to add that question mark there so now we'll have a reference to that document and we can now call get on that document to actually get the document so this this line up here is just going to get us the reference to the document and then this will actually get us the document and then we can call a then on this and once we have the document this value here is going to be the document snapshot and I will convert this to a block and then within this block we first want to check that the document snapshot exists which it should and then once it exists we want to set this document data which the data we're going to set in this is that bank and we're going to set that equal to three so this is going to be the default Bank value so this initialized account anytime it is called is going to update this document for the user to set their Bank equal to three so we want to make sure we're only calling this when we create a new user and luckily up here in our get or create user we're already we already have that logic set up so only within this if block here we are going to create a user here anonymously and then after we create the user we can initialize their account with that three in the bank so if we save this and rerun our app you're going to notice nothing actually gets updated into Firebase and that's because this user's account already exists so what I'm going to do is actually just delete this collection we did delete all that users data however we didn't delete the user itself so we need to go into Firebase authentication and delete the user for for this account and now we no longer have the Firebase user so when we run this now it's going to recreate that user because we no longer will have a current user so after rerunning the app we should get that user created again so if we check on Firebase here we actually do not have that new user yet so if we you'll notice the user is still being red as the one that we just deleted if we go to the device up here and eras all Conant and settings we can clear out all of the stored information on this device which currently for that app it is saving the user as that current user and this is something that Firebase does for you automatically it will keep track of the device and the user which is which is pretty nice actually and it it helps with offline mode and and things like that but when you're trying to do what we're trying to do right now it actually interferes a little bit because we did delete that user from Firebase but it wasn't deleted entirely yet in the app I think over time that that local cache of the user would expire and then it would actually be gone but since we're trying to do this all at one go here it was still available on the device so now you can see when the app loaded up we have a new user ID down here and if we go into Firebase you can see we will have a new user created and then we're also going to be able to look in Cloud fire store and see that our new user has the default Bank value of three so that's going to be it for this video we now have our account modeled out and then we also have our account initialized with that initial Bank value of three in the next video I'll show you how to hold this value and display it using a stream builder in this video we're going to set up a stream Builder to actually listen for changes to our Firebase user so for instance if the user buys an Ina purchase and gets more decisions the decisions are going to update immediately because the stream is always going to be listening for those changes let's get started so in the last video we set up our account model here which is going to store the bank for the user and in my current user right now the bank is set to three which is our default that we initialize the app with and we want to display that three up here so we can open up our home view now and we're going to require an account to be passed into the home view when we create a new instance of the home view so we'll Define this as a final account and we'll call it the account and then we are going to need to initialize the home view with that account we're going to make sure that this is a required parameter so let me write this out all right so at this point our account will be required on the home View and we can simply now go to where our decision is right here we can use the bank on the account here so if we do the account which is actually going to need to be prefaced by widget and then we can just call the bank on it and it's going to display that account balance now so now we need to get this account passed into the home View and we're going to use the stream builder for that so if you go to main. Dart you're going to see we now have an error on the home view because we don't have an account being passed into it the reason we're going to use a stream Builder here is because the account information is stored in Firebase and we're going to need to make that call to Firebase to get that account information and since that's going to an asynchronous call we need to either use a future Builder or a stream Builder the benefit of using a stream Builder here is whenever any information on the user's account is changed we're going to be listening to that change through the stream Builder and it will automatically update the app so what this means practically is that when we use the stream Builder if we were to for instance go into Firebase here and change this value to five or some other number it's going to push that change to our stream Builder and then it's going to update it in the app essentially immediately so that is actually really cool and it's going to be helpful later when we change the value of this Bank from A Different Page it's going to stream it directly and update the app everywhere that that account value is referenced so let's go ahead and set up that stream Builder now we can go ahead and comment the home view for a bit and create a stream Builder here and the stream Builder is going to take two parameters the first one is the stream and the stream here is going to be that asynchronous call essentially to Firebase so we're going to get a Firebase fir store instance and then we're going to call the user collection it's users actually the users collection then we're going to get the document for our current user so we need need to get the current users ID and in the past we set up this off service through our provider so we can use that off service on the provider actually and we'll use that via the context and then we're going to be reading the context value of the O service and then we're going to call current user and uid and lastly we're going to need to call snapshots on this so we're going to be getting the oh and I missed the closing bracket there so we're going to be getting the snapshots for this individual document and that's a little bit better formatted there the second thing that the stream builder takes is the Builder itself so once the stream is has some data or any changes are made we can actually unwrap the snapshot here and and do something with it so we will have access to the context as well as the snapshot and the first thing we're going to want to do is check if this snapshot has any data and if it does have data then we then that means we have our account data so we can actually create our account object right now so this will be a final and we're going to call it the account actually we can set this as an account and we'll call this the account and we're going to be getting the account from the snapshot we need to import the account package but if you remember we did create this when we created our account class we have our from snapshot which is at the moment just going to take that bank value and create an account based on that from a snapshot so our account from snapshot here and we are going to need to pass it the snapshot data the snapshot in this case in this context here is actually not including is not exactly what we want to pass into our from snapshot we only want to pass in the data from this snapshot so we'll pass it in like that and this in its current state should actually be enough but the second thing that you will notice is we also have this uid on our account here and we if we want to actually initialize our account with the uid here we can take that as a secondary parameter here and then we can just set the uid equal to the uid that we pass in the value of this which I briefly talked about in the past video is that once we have an account object we'll easily be able to just get the user's ID without having to go back through and get it through the current user uid both are essentially going to be available but it's kind of nice to just be able to call it directly off the account so we can take this uid parameter here and just pass that in as well down into the account object now so that is kind of optional I think it's easier to deal with the uid through the account but either way will actually work the final thing we want to do here is actually just return from our Builder and we're going to return the home View and with our recent update the home view now requires an account but luckily we have that account defined right there so we can just pass that in and I'll delete this comment down here all right so we're getting an error about the key being required and this I think is just actually a problem in the syntax here we can just delete this whole key key part here and essentially just Define it by only requiring the account so this is a little bit simpler definitely a cleaner way to do it and should allow us to have this working now we do need to return something out of here if the snapshot does not have data so down below here we can return and we can set this to be a circle progress indicator or possibly even just an empty container but if you refresh the app now you'll see we do get that three value there because the stream Builder is pulling our Firebase snapshot which is our user here it pulls the value from the bank there and creates that as an account object and then that account object is passed into the home View and the home view takes that account object and as we updated in the beginning sets that bank value right here so one last thing I'll show you with this stream Builder which is pretty cool is how the real time updates work so if we were to update the bank number here from 3 to five and hit update you can see the update is almost instant within our app so this is very powerful and prevents us from having to refresh this page in any way when that value changes so very useful stuff there and it will work for any value that we have in our account model really any value that we're going to be pulling from this document here as long as we tie it into that stream Builder in the last video we set up our stream Builder to actually show this value here which is related to what's in the bank on the user's account but when we actually ask a question it doesn't do anything and it doesn't deduct from that bank so that's what we're going to set up right now so we're going to do this right here in the answer question function and this is called whenever that ask button is clicked So currently we're updating Firebase with the question and the answer but we're also going to now update the user's account so we can access the account with widget. account and we're going to just set that bank value to a minus equal one so it'll just subtract one decision from the bank and you can see we're actually getting an error here and the reason for that is potentially the bank could be null and since we have null safety on now it is throwing that error there we're going to actually make it so the bank value just has a default value so in the account model over here let's just set the default of the bank to be zero and make this no longer be able to be null so now this should work with the widget bank account being subtracted by one but that's not enough we actually need to also make a call to fir store to update this so similar to what we have down here we're going to make another call but we're only going to be updating this document here so we can actually just copy this whole piece here we'll make this call below we can do I suppose that whole call and then we can do this whole stuff down here to update the document so once we have that document we're going to just call update on it and the update here will take that key value pair of all our parameters which in the account model we actually already created that two Json and currently it will just have the bank in it so to use that we can just call widget. account. tojson and if you save this and now ask a question you should notice this three is decreased to two which as you can see that worked a few things we can do now to update this firstly you'll see we're using provider here to get this uid but in our account model we also have the uid and we are saving that uid there so while this is fine and there's no real reason to change this you can also use widget. account. uid and get the uid that way and since this function also has that same access we can do the same thing here it's just personal preference this is a little bit cleaner in my opinion than going through that provider but you might not always have this option to get the uid from from the op you're dealing with so that's good there and now when we have this decision here and we get down to our zero decision we don't want this to go negative we actually want to check when this gets to zero and then actually not allow the user to ask another question so we're going to start setting that up right now and we'll finish it in the next video but what we're going to do is actually start tracking when the user should receive their next question and we'll do this every time that the user asks a question so every time we'll just update it and say in 8 hours from now you can ask in 8 hours from now you can get your next free question but if they already have questions it won't matter the timer will just update and we won't really use it but we will always be updating it so we're going to update our account model here to actually store that that date value so this will be a date time and we're going to call this the next free question and we're just going to want to add this into our Firebase getting and setting all right that looks good but we will make this nullable so add the question mark there and that looks good so now back in our update here when we're updating the account we're going to actually set that value so set next free question and we're going to set that equal to the datetime do now and add a duration to it so we can add a duration here by calling add and then duration and for the sake of our test I'm just going to set this equal to 5 seconds so for right now I'm just going to set this equal to 5 seconds so while testing we can get kind of a quicker new free decision but in the production version I'm going to be sting this to 3 hours so basically after a user uses all their decisions after 3 hours they'll get one free new decision but if we save this and ask another question all right so we are getting an error here and the error is actually in our account model and where we're calling next free question from snapshot Firebase is going to return a timestamp and we need to convert that to a date time so this is pretty simple we just need to add a two date here and we can actually add a question mark before this because not every snapshot is going to have a date time so if it does then we will if it does then we will convert it to a date and if not we just won't even do anything so if we rerun the app now you'll see that question did go through and that means our Firebase update should have gone through but let's go ahead and just ask one more and you can see that goes to zero and you can see actually our time stamp was also updated so this time here is actually 5 seconds from when we hit that button which is basically the current time right now so this is good and we are saving this data how we would like in the next video I'll show you how to actually hide all this stuff once we are down to zero and actually show a countdown timer and give that next free question once the countdown has reached zero so you can see here our user has zero decisions left and when that's the case we don't want to show this form so first let's go ahead and hide that when there are no decisions on the user's account we're going to create a new void function here and this is going to be called set app status and we're going to have two statuses we're either going to have the ready status or the waiting status the ready status will be the normal app the user will be ready to ask a question and the waiting status will be they have no decision so they're waiting for their free decision we can Define these two statuses as an enum so up at the very top here above our home view we're going to just add an enum and call it the app status and we'll give it two values which are ready and waiting now back down in this function here we can just do a quick check so we can check check if the accounts bank which we get two widget account bank if this is greater than zero then we're going to set the app status to equal ready we'll do this with set State and we're going to create a new local variable called app status and we'll set that equal to app status ready we now need to just quickly Define this app status and we'll do that right here below the question and this will be of type app status and we will allow this to be null so we'll add that question mark there lastly We'll add an else here and set the state to be the app status of waiting all right great and this set app status now we're going to want to call every time the the page is built so we're going we going to call this right from the build method itself so if we save that not much changes but we want to go find now where we're building out this form and that is right in this build questions form and we're going to just wrap this whole thing in an if statement so if we shrink that whole thing down we can just say if this app status is equal to app status ready then we're going to show the form when it's not ready we will still show the last question and answer so currently in this column towards the end we do have this function to show the question and answer which is just this widget here and it's going to show the last question and the answer that we gave to that question so if the app is waiting we will still return that piece of it so we can add that in right here and now you can see it removes that form if we had a question that was asked before we did this then that question and answer should show here so actually let me just show you that example if we go in here we can add one to our bank here now we have the form showing again because this is now in the ready state so we ask any question and click ask and you can see it does show us the question that we asked and the answer there and then it zeros that back out so this is good we now have our enum set up and we have an app status set up and then we're able to also use that app status to show different parts of our app depending on if the right conditions are met in this video we're going to set up the countdown for the next free decision so the way the app is going to work is a user will have a set of decisions when they start and once they use all of those decisions by asking questions to the app they're going to end up with no more decisions and at that point we're going to count down from any any amount of time we're going to use three hours but in testing we'll use a shorter amount of time and we'll count down until they get a new free decision so after that time has lapsed they'll get a new free decision in their account so this way the free account they can continue to use the app without having to pay for an inapp purchase it'll just be a more delayed less good user experience so let's get started so when our user has no decisions left we are setting when they should get their next free question but we aren't actually showing them any sort of countdown or actually even giving them the next free question so that's what we're going to do right now the first thing we're going to do is create a new widget here and we're going to call this the next free countdown and this will return a column and first we'll have a text area and this is just going to say you will get your next free decision in and then below that we're going to actually have a countdown of the time so first let's add this into our B build widget so we want this right below the decisions left so if we go up here we can find where decisions left are and then add this in so that looks good there before we even show anything here we want to make sure we're only showing this when the user is in this state so we can check to see the status of our app which is app status and we can see if that is equal to app status of of waiting and if it is then we are going to return this countdown here uh if it's not we can just return an empty container so to get this actual countdown showing we're going to use this package called timer countdown and what this is going to allow us to do is pass in a a large number and then it will just count down from that number so there's a bit of an example here but let's just go ahead and install it so we want the version 2.2.0 and if we open up our pspec do yaml we can add that in and then save and run Pub get now that we have that package we can actually use it with the countdown widget now and this is going to take a few elements here so firstly it's going to ask for the number of seconds to count down from and then it's going to take a build so a number of seconds here we can set this first and we're going to create a new variable and we'll call this the time till next free we can't pass directly the widget account time till next free because that is formatted as a datetime so we actually need to format this into seconds and we can do that on the page load here so firstly let's create a new int here and it will be that time till next free variable and we'll default that to zero then we're going to create an init State here and within this we're going to actually set this time till next free and we're going to set it equal to the widget account and then get that next free question timestamp which this is a date time right now and if that exists then we're going to get the difference between that and the current time and then that difference we will convert to seconds and if this doesn't if this doesn't return anything then we are going to just return zero and we don't actually need this question mark here so now we have this time till next free that should be set properly now the next thing is this build here and this is going to take a context as well as a Time the time will be a double and the context will be a build context and this time as a double we can just return that and we will format that in a bit because it isn't going to be looking exactly how we want it to uh the next parameter here is the interval which we can set and this takes a duration so we can give it a duration of hours minutes seconds but we're going to set the duration to 1 second so it will'll count down 1 second at a time this here actually needs to be a widget so we're going to wrap this in text and then we will give the time so that should work the last thing we can do here is set the on finished so this is going to be called once the timer has gone down to zero and in this case we're going to just call a set state for now and we're going to zero out that we're going to zero out that time till next free because even though the timer will be CED down it's not actually going to set the state of that to anything un unless we reinitialize the app and then lastly we are going to set that App State and we're going to set it equal to ready so you can see up here it was waiting but now we will be in a ready state so if you rerun this you'll see we actually have a negative value here and the reason for that is our time till next free decision has already passed so let me update that to a time in the future so I just updated that to a time a few minutes from now and you can see the timer is now counting down there are a few things that we do want to be aware of the countdown isn't always by default started so we do want to make sure it is going to start by default and we can do that with a controller so we're going to create a countdown controller you might not be thinking this is necessary because it is counting down right here in the simulator but I found that on the actual devices it doesn't countdown immediately but then sometimes it was so it seemed I don't know it seemed a bit inconsistent with this package but I found this way to always get the countdown to start so we need to Define this controller up above and this will just be a countdown controller so now that that's defined like that uh once we get into this function that we're creating the next free countdown we can set the controller to start and that I found solves this problem you can use this countdown in multiple different ways and have buttons that stop it pause it and do things like that which if you do use that then that's where the controller is actually going to be super useful but for here this is all we needed for the next thing you'll notice is the way that this is formatted it's just seconds but we really want to format this in a a readable timestamp and then countdown through that with to actually get our numbers formatted we want it to be two digits for the hour and then two digits for the minute and two digits for the second we can use a formatter which we can Define right here and we're just going to Define that as f for formatter and this will just be a number format and we can set it to the two zeros because that is what we want we want two decimal places for each and then we're just going to this is just going to be in English for the us and that second parameter is just telling us what this number formats language is and we will have to import the inl package to be able to use this number format but now that we have that format set up we can now take this value here and split it into its three components hour minute seconds and so I increased it so now we'll have some hours minutes and seconds the text here where we have the time let me actually just copy over this new formatted one and I'll explain it because it is just a little confusing so that is updated and this looks good but let me run through this real quick for you so you can see it's three different columns or you can see it's three different elements of time here and we're doing that with just these three elements within this string so it's three different variables that we're setting each with a colon in between so we're really taking this time element here and we're manipulating it three different times the first time we're dividing it by 3600 which is going to get us the hours and using the division like this it's going to only get us the solid hour so if it was like 4.2 hours this is just going to return turn four and then since we're using the format here the f. format here it is applying this double digit format to it so that's why you see Z4 here and then as you go down it is a little bit different but the similar concept this time we're going to take the time and mod it by 3600 so this this right here is going to actually kind of give us the leftover piece whereas this was going to take in the example if it was like 4.2 this one is only going to take the four this one is only going to take the 0. 2 and then we're going to take that 0 2 and again this is going to be in just seconds though so that amount of seconds that would be left then we're going to divide it by 60 which this this part here is like what we did over there where we'll just get those amounts of minutes and lastly this one here when we mod it by 60 it's going to remove everything except those final seconds between 0 and 60 so that formatter look good and this countdown looks good so the only thing we need to do now is see if it actually works so let me set this timer to be much closer in time all right so we now have about 10 seconds left and once this countdown ends we're going to expect this on finish to get hit and then update the app status to that ready status which then should show us the ability to ask a new question however because we did not also increase the decisions left the app status is going to then immediately get set back to zero because we have no decisions so to fix this we just need to increase the decisions Now by one so we're going to create a function that is going to increase the decision by one as long as this next free time is zero and the bank is zero as well so we're going to want to call that here on the unfinished but we're also going to call it when we initialize the app because if you can imagine someone sees 3 hours left and they leave the app when they come back to the app we want to first check if they should get a free decision because the countdown is up and if that's the case we'll just give them the free decision immediately so keeping that in mind we're going to create this function to work for both ways and to do that which we're going to call get free decision to do that we're going to take in the current balance so the current bank and then we're also going to take in the time until next free and with this now we can do a check so we want to basically when we call this we still want to check if we should give the free decision and the conditions when we would give this is if the current bank is less than or equal to zero typically it would never be less than but if it is we would give a free decision and we want to see if this time next free value which will be in the format of seconds is also less than or equal to zero which means it's time for them to get a free decision and if that's the case we're going to make a call to Firebase so we need a Firebase instance here and then we're going to call the collection of our users and then we're going to get the document and now we need the users ID which is accessible through the account or you could go through provider and then finally we're going to update the bank here and since we know that the user's current bank is zero we're just going to update this to a one we're not going to increase it by one we're just going to update the bank to one so we'll set that bank value and we'll set it equal to one and that is it for that now we need to call this get free decision when the unfinished is done so call get free decision the current bank is going to be the widget here of the account. bank and the time till next free in this case it's going to be zero you want to be sure not to actually use this variable at this point because we didn't zero it out yet so at this point if you passed in this variable up here it would still be whatever it was up here and it would not actually give them the free decision but let's take this as well and go up to our initializer and also call it from there so if we paste that in we are going to need to modify the zero here now and actually use that time till next free so if we save this and rerun the app so that this initializer gets hit it's actually going to give us that free decision because we're making the call right here and then since we have this set to a 5-second timeout if we try this one more time and hit ask you should see a 5-second timeout and then it comes back although that did not look correct with what we had on our timer there so let's go ahead and set this to a minute all right so I updated this to be actually a 20 second duration so let's go ahead and try this and ask a question and you can see it's actually not quite displaying this time out if you refresh it though it does it does then load it in but but there's an issue with this timeout being set correctly and that issue is that we set the time till next free we Define that up in this initializer here and it's going to be the value from fir store but this isn't going to get updated so we need to so we can easily manually update this when we're saving our data to fire store so you can see right here we're setting our next free question and we can also set the state here of our time till next free and we can set that equal to this this updated variable here of the widget account next free and you want to make sure to call this after you have this line set uh but we do need to remember that this needs to be converted to the difference between the current time and this time so if we go back up to the top you can see where we were doing that where we call the difference and then convert it to seconds so let's go ahead and actually do the same thing down here so now we'll have that time till next free set another thing we can set here is actually that app status so we can check if the widgets account bank is zero and then we can go ahead and set that app status to equal The Waiting status now you might be wondering why we're even doing this because it already was working when the when the account went to zero it was already setting the status to waiting this is going to just speed that up so you might have noticed there was a little delay but now it will be let me comment this out and you can see the delay so right now the timer should be working so if we ask any question and hit ask you can see we now get that 20 second countdown but you might have noticed it was a little bit of that delay and if we uncomment this out here that delay should be gone away because now we're going to be using the state of app status so it will be instant what we were doing before was having to listen to the update from Firebase and all of that reading and writing does take a little bit of time so if you try it now and hit ask you'll see it was immediate and this all was just immediate it in this section you're going to see how to monetize your app with ads we're going to be using Google admob to place the ads in our app and we're first going to get set up with some banner ads and just have a banner ad at the bottom then we're going to put the banner ad in our history list view after we've done the banner ads we're going to go ahead and do an interstitial ad which will pop up after someone asks a question then we'll set up the reward video ad where a user will be able to click a button and watch a video and after watching the video they'll get two decisions lastly we'll set up native ads and the native ads are going to replace the banner ads in our list View and simply make them match the style of the other cards in our list view so let's get started adding ads to our app so the first step to adding ads to our app is actually configuring it with Google admob we're going to be using the Google mobile ads package and this will give us all the types of ads that we would like from Google admob so firstly we do need to actually configure our app in Google admob so if we go over to Google admob here you can add a new app and we're going to need to add both an IOS and Android so I'll do the iOS first and it's going to ask if it's listed in the store we can hit no for now because it isn't currently I'll call this decider course since I do already have the decid one set up uh then we're going to add add that app there now you're going to see that this was successful we're going to ignore the app ads. text for now we can set that up later in another video uh but we are going to want to create some ad units so in this section of the course we are going to be creating the banner ad the intertial ad and the reward video ad in a later section we're going to come back and set up the native ad as well as talk about the app open ad so go ahead and select the banner ad for now we're going to to just name this the banner and we can create that add unit now you'll see here we're going to get some IDs so this first ID up here is actually going to be the ID of our app in admob and then this one down here relates to actually that Banner ad we just created we are going to be using these in a bit so you can just hit done and we'll be able to get to them later I'm also going to add an ad unit for the interstitial and I'll just call it the interstitial you can name these a bit more specifically if you would like and lastly I'm going to add one for the reward now depending on how many different types of reward ads you plan to have in your app this could become useful but we're just going to be relying on the app unit name and ID to basically have that this specific reward always give the same type of reward but this down here could be filled out to make it easier for you to manage those if you have multiple different types but we'll leave it as it was and hit done so now you can see we have the three different types of ads here and this is only our IOS app so let's go ahead and add a new app for Android as well and this will basically be the same thing and we'll create those three ad units as well all right great so now everything in admob is configured how we're going to need it in the next step we're going to actually configure our app to use these ad unit IDs so the first thing we need to do to get ads actually displayed in our app is install the package so you go to the install instructions here you'll be able to see the command here and if we just add this into the terminal you'll see we'll actually add right into our pubspy the actual Google mobile ads package and at the time of recording this the version I'm on is 0.13.5 but if you're watching this a bit later a newer version may be out and I would suggest using the newest version however one thing to note if you are watching this much later and the version is significantly different there may be breaking changes within that version and if you go over to the change log here you can see if there are any breaking changes they'll be listed here and how to fix them and if there are very large breaking changes I will be adding an additional video to this course to talk about upgrading to that version so we have that just make sure you run Pub get after that and now we can configure the app so firstly we'll set it up for iOS we're going to go into the iOS folder here and then the runner and then find the info pist and there are two things we're going to need to add if you look in the read me here you'll see what they are and we can actually go ahead and copy these and then modify it so we want to make sure this is within the the first dict here and we'll just place that then towards the bottom uh this first key here is actually going to be the identifier that we just created in admob so this one here is a test that we're going to want to change that so back in admob if we open up our IOS app here we can go to app settings and we're looking for this app ID here so we're going to copy that and replace this value with that value then you'll see the network we do need this SK ad Network items and this basically just tells the iOS builds that we will have ads in our app so we're going to save that now and if you were to view this in xcode you could find that same file that info pist file and you'll see now we have the the same information here so that should be it for iOS we can X out of this and now we're going to configure it for Android which if you open up the Android file here we're looking for the Android manifest.xml that's going to be under the app Source Main and if you find this and scroll down towards the bottom right after this metadata tag for flutter embedding is where we're going to add one more metadata tag and if you are following this documentation you can see it is this right here so again this is using a sample value right here so we're going to replace that with the one that we just created so if you go into Google admob again make sure you're not using the iOS version here you can see I'm on the IOS app right here you're going to want to change that to the Android version so now you can see I'm on the Android one and and this is the reason we need to create two different ones is because we will be tracking these separately because they will be actually two different apps and if you use the iOS one for the Android one it is not going to work and vice versa so at this point both IOS and Android are configured at least to be talking to admob in this video we're actually going to initialize admob and then also set up some helpers that will allow us to easily access the ad unit IDs regardless of which type of AD we're using so to begin we're going to create a new service class here so similar to how we set up our off service we're going to create a new file here and it will be a dart file and this we're going to call our ad mob service and this will be just a class we will initialize this with a future and we can set this up just like this so what this is basically going to allow is for us to pass an instance of the mobile ads instance that's initialized into this admob service so how this is going to look and we can actually set this up now on Main if you go into main here we're first going to create an admob future which is going to initialize the actual mobile ads package so this will be a final variable and we're going to call this the NIT ad future and this is going to equal the mobile the mobile ads instance and then we can call initialize on this instance here and that will be a future so that is why we Nam this the future but then we can go ahead and initialize that new admob service that we created and we'll call this the ad mob service that will be the admob service and we can initialize that with the with the initialized future here so basically what this is going to do is just allow our admob service to have access to this initialized um mobile ads instance the last thing we can do in the setup here is we have our multi-provider setup and we can add another provider for our ad mob service and then we'll be able to just use this ad mob service throughout our app the via the provider here so let's change this slightly uh if we add a comment here so we're going to be adding a new provider and the provider will actually just be this ad mob service here so everything on Main looks good and we should be initialized with this here the other thing we can do in this admob service is actually just generate a few of the helpers that we're going to need and then in the next video we can actually create our banner ads there's basically three Getters that we're going to create and it's going to be to get the ad unit ID for each of the different types of ads we have so this is going to be a string and it's going to get us the banner add unit ID the banner ad unit ID could be one of two things but really could be one of four things because we firstly we created an iOS in an Android version of the app so we have two Banner IDs for production but then we also want to be making sure we use our test Banner ad unit IDs while we're testing and this getter here is going to allow us to put all that logic right in one spot and then always just call this to get the banner ad and it'll know which device we're on and whether or not we're in production or development and give us the test ad unit ID if need be so firstly let's check if we are in production and it we can use K release mode and this is going to return true if we are in production or rather if we are in the release mode which is what the production version of that will be and next we are going to check if what the platform is so if the platform is IOS and we will have to import Dart IO there but if the platform is IOS then we can go ahead and return our IOS app we're going to need to get to that decider course here for iOS and we can go to the ad unit IDs and we'll see right here is the banner add unit ID so we'll paste that in here and then if it's not iOS then that means it's Android so we can return the Android adun ID all right and so this this is our production values here but we we're going to add an else here and this would mean we're on development and we're essentially going to put the same thing here but we're going to use the test ad unit ID so for iOS this is the test add unit ID and I'll link below where you can actually find all of these and this is going to be the Android test ad unit ID so this concept right here of getting the banner ad unit ID we're going to copy and do it for the interstitial ad unit ID as well as the reward so I'll just fill these out now but I think you understand what you're going to want to fill in here all right so I updated this with all of those IDs one other thing I changed is I added an else if instead of just an else to check that this is actually Android this isn't entirely necessary but if you are going to have this on other platforms such as the web then it is nice to have that just explicitly set there uh and then if neither of these returns are hit we can add one last return to just return null we are going to be using these Getters here in just a bit but now our admob service is looking pretty good in this video we're going to finally use what we have configured and actually add a banner ad we're going to add it right down here at the bottom of the screen so let's get right into it now we're going to be on the home view here which is this view right here we first need to actually create two new variables one for the admob service and then one for that Banner ad so right here on the home view State uh we can go down a bit and these are going to be all our ad related variables and there will be a few as we go firstly we're going to use this keyword late here which is basically going to allow us to initialize this admob service but not make it basically it lazy loads it so it's not the primary thing that is loaded but it is telling flutter that this does need to be initialized and it will need to and it is a non-null value that is kind of a newer thing that late keyword if you haven't seen it um add mob service here is what we're going to name the variable and we're going to be using that underscore because it will be scoped to just this home view State secondly we're going to add a banner ad and this Banner ad type is actually coming from the package that we installed and we're going to just call this we can call it a banner ad here and this Banner ad is has we use the question mark here because this actually can be null so there is there is a situation where Google admob is not going to give us a banner ad even though we request one and if that's the case it's okay we just want to make sure we don't try and display a banner ad if we don't actually have one so that is why that could be n now we can go ahead to our we have our init State here but we want to actually add some code into our did change dependencies so let's go ahead and add that override here the first thing we're going to want to do is actually set this ad mob service and again we did set this up with provider so we're going to just set it to the context of reading our provider for admob service so so now our admob service local variable is essentially going to be the the one that was in provider so next we're going to take this admob service and call initialization on it and then we're going to actually call then so once that future is initialized we're going to have a value here and we're going to convert this to being actually a block and then within this block here we can do a few things so firstly we want to get that banner and actually set it so this Banner add up here and I'm actually just going to change the names of this to be just banner and we want to basically Define that banner and load the banner and then once the banner is loaded in this to change dependencies we'll have the state of the banner set and then later within the build context we can just actually show that loaded Banner so basically Banner will be null until a loaded Banner ad is ready so simply we're going to start with a set State Here and Now within this set State we're going to be setting the state of that banner and it's going to equal a banner ad and now this Banner ad is going to take four parameters the first one is going to be the ad unit ID which if you remember we already configured in our admob service which we can look real quick in our admob service here we have this Banner ad unit ID so we're going to be calling that directly on our admob service here and because this could be null actually if you remember this code could return null we do need to use that exclamation point at the end there the second thing is the size there are a few size options here but we're going to be using the ad size of the full Banner ad you can see are different options right here we're going to use full Banner ad so it takes up a wider width then then we need a request here and the request is simply the add request and this is just part of the built-in package and lastly we need a listener and The Listener is going to actually live in the admob service as well but we did not create it yet so we're going to call it the banner listener and now we can go ahead and create that so go back into the admob service and down towards the bottom we're going to create this Banner listener as a final here and it will be a type of banner add listener and we'll just create a banner ad listener here there's basically four different properties that you're going to want on your banner ad listener and this is in the documentation so I'm not going to type it all out basically you want these four options here and each of these four are going to actually tell the ad what to do depending on what happens so on the ad load currently we're just printing this but if you want to add any sort of tracking or any sort of other event that happens when this Banner ad is actually loaded you can do that here uh same with failed to load this one is a little bit different because if it does fail to load we are going to actually just dispose the ad and then last is just an onad open and on ad close you can maybe use this if you want to add some tracking really if you want to do anything specifically if someone actually clicks on one of your ads we go back into our home view our Banner ad is good now we can call load on this so there's two ways we can do this we could either Define this Banner ad and then call load on that or we can use the dot do load which will call load on this and return the value of load so that looks good now our Banner will be set if one does load and since we're using test at unit these we should always get one loaded last thing we need to do is actually add into our widget Tre the banner ad itself so down here where we have this text which is basically giving the current uid we're going to go right below that and that's where we're going to add the banner ad depending on if the banner ad is null or not there are two options which could happen here so we want to use an if statement and we can just check if that Banner is null and if it is null then what we want to do is actually basically nothing we just want to put maybe we can put a little bit more space in here but we don't necessarily want anything too crazy going on if there is null so we can just put a sized box here and give it just the height of 20 and if you save that you'll see we just it just bumps this up slightly uh which is fine uh if you don't want to bump it up at all you can just not even have this so in the else here which will mean we do have a banner ad we're going to create a container and this container will take a height property and we can set the height here to the height of what our ad is going to be so if you look up here to the size you can see if you hover over this the full size of this is going to be 468 by 60 so we want the size of this to be 60 the height of this rather to be 60 and then the child of this container is simply going to be an ad widget and this ad widget is something that is part of the package that we installed for these ads and the ad widget takes an ad parameter since we have that Banner ad stored in this Banner that will work to pass that into it however this can be null so we need to explicitly unwrap that and if we save this now reload is not quite getting our ad there but if we rerun the app we should see a banner ad displaying all right so you're going to need to completely rerun the app or at least I did but now you can see we do have a banner ad loaded up right there and just to show that this is also working on Android we're going to run the app on Android and just make sure that Banner ad is loading there if the banner ad is loading there that means we configured both our IOS and Android correctly so it's worth testing on both devices at this point and you can see here the app is running on Android and we do get that test ad for both Android and iOS so at this point our app is definitely configured with admob and we have our Banner ad set up the next video we're going to see how to add a banner ad into a list view in this video we're going to see how to add a banner ad to a list view So currently we have a banner ad down here on our homepage and it won't move anywhere it's always going to be locked into that position down at the bottom of the screen but if we go into our history list view here you can see we have this list of all these past decisions and what we're going to do now is add a banner in every few cards here so we'll have a few cards and we'll have a banner ad and then a few more and another Banner ad and this will illustrate how you can kind of reuse banner ads and put them in multiple locations throughout your app programmatically and it doesn't necessarily need to always be in that same position so opening up the history view here we have this history list which essentially ends up becoming this list of all these items which we're pulling from fir store if you remember and look back in the code we have this get users questions list here and right within this function here we're going to actually just write the code to add those banner ads within the list after we set the history list here here in set State we're going to add banner ads so the first thing we need to do is actually get our ad mob service we can pull that from the provider and then similar to how we called it on the homepage we're going to call the admob service here and call initialization so once that's initialized we can create a new Banner ad and we can insert it into the history list so we want to insert these at a Pace that isn't every other card and maybe we want them every five cards but we don't want them to take up the entire list these ads so we only want a few of them the way we can do this is using a for Loop so we're going to have Define our in I which can be called anything and this is basically where we're going to start in the loop so we're going to actually be doing a a reverse loop on this history list we want to make sure we're not placing ads outside of this list so if we start at the bottom we're always going to know we have up to zero to go and it makes the logic just a little bit simpler than trying to always check that we still have room in the list so if we take the history list and get the length of it that means currently I is going to be the last element in the list so if we want to make sure that the last element in the list is never an ad and we always want to make sure maybe the second to last would be the potential last place for an add or the third to last we can take the length and subtract it by three so at this point length subtracted by three means that the third element in our list here is going to be potentially an ad but never will we have an ad down below at the very bottom and now we can set kind of a similar thing here where we check for the Top Value up here so cuz as we're going to be looping we're going to want to check is is there still room for a new add up here so for this we can just say if I is greater than or equal to 3 as well then that is going to mean we have space up here for another ad and lastly we're going to have to set what we want to Loop over so how many rows are we going to skip as we go through this Loop if we do a minus equals 5 here that means I is going to continually be jumping over five blocks before the next time that this Loop is called if that is a bit confusing to you once you see it you'll be able to play around with the numbers and then also kind of get a feel for it so once we are within this block which again this block is only going to be hit when these when these settings are met then we're going to get our history list we're going to insert into it and insert is going to allow us to put a new item in the list without removing any items that are already there so we're going to be inserting at the index which is this I value here and now we're going to be inserting a element and that element is going to be a banner ad we can call our Banner ad here and just like what we set up in the home view we can set up this Banner ad with the four properties that we need on it and then we can call load if we go back to our home view we can look at where we defined our Banner ad right here and basically we can just copy this because it will be all the same stuff and let's just paste that right here and we're going to need to in that there now our admob service here doesn't have the underscore so we just remove that and if we end this with a comma we should be looking pretty good here um actually that comma is going to be be here all right so if we save this we're actually probably going to get an error screen if we try and rerun this and view our history list View and the reason for that is because our item Builder here is assuming that a question card is going to be the item always in the list view that's not going to be the case anymore because potentially we're going to have a banner ad as an item in this so when we get up here in our item Builder it's going to say here build a question card and take this ad this Banner ad and that's going to fail because the banner ad won't be able to be converted to a question so it's not too difficult to actually check what type of object this is and then we will display accordingly so we're going to check if our history index here is a question and you can use the is question here and that is going to check if this if the object at this index is a question and if that is the case then we're going to do what we were doing and display the question if not we can do an else if here you can do an else or an else if uh since these are the only two types currently we can use the else and we will actually wrap this as well in these brackets so now since we know this is going to be an ad we can return a container and we can give it that height and again we're going to use 60 here because the ad size that we're using has a height of 60 and then we can set the color here and the reason we're going to do that is so the background here matches the background of our page so we're going to use color do colors. white and lastly we're going to set that child as the ad widget just like we did on the homepage but now this ad is going to be the history it is going to be the history list index so it's going to be the object at that history list index and then just like we were converting the other one to a question we can set this one to a banner ad uh which it will be uh if you wanted to make this an else if just to be safe you can and you can say else if the basically the same thing here history list at that index is a banner ad then you can put the banner ad there and then finally you will need to add an lse since we are using that else if and if there if it's neither of those types we're just going to return an empty container and that will allow this all to work for both the questions and the banner ads so the app did crash and we tried running it before which was semi expected because this list view was not going to be able to be built out but now that we have this set up if we try running the app we should see that we have banner ads positioned essentially every five starting from the third index and at Mo and at most being close to the top third which will make more sense when we see it if that's a bit confusing still so this looks good let me try and explain this now with this visually so you can see we're starting at the length minus three so we're going up by three and this is our first Banner ad that's going to get placed and then we'll go up by five and place another one and since is greater than or equal to 3 which means we have more than three above it we will place the add now if we were to change this to one this first one to one you're going to notice that the and you do have to go out and come back in you do you'll notice that the first ad now is in the first location to the bottom so it's basically the the the second to last one and now we're going to go up by five which is expected and we'll get an ADD and then we're going to we're going to go up by five again and this time we don't get an add here and the reason for that is because because of this parameter right here the second one where we're saying if I is greater than or equal to three which at this point I is not because I is really only going to have there's only two elements above it so we're not going to have an ad placed here because of that now you can change these numbers arounds and play around with it to get it the way that you like it but I'm going to leave it at 335 for now I think when there's not a lot of when there's not a lot of questions 335 is good but probably realistically this five should be more like a seven or an eight but that is going to be it for this video you can now know how to display ads in a list view in this video we're going to set up an interstitial ad so an interstitial ad is one that will show up and be a full screen ad after some action is performed so we're going to set up the intertial ad to happen after a question is asked so in the home view here we're going to create a new variable called the interstitial ad and that is going to be an intertial ad type so very similar to how we have the banner ad set up where we're going to be doing the same thing with the interstitial so then similar to how we're defining the banner ad here in the set State we're going to do the same thing for the interstitial but to keep things a little bit cleaner we're going to create a new function and it will be called create intertial ad and now we can Define this towards the bottom of the file so this will be a void function so we can do it right below these other void functions and we're going to have this load up an interstitial ad and this will take three properties the first one is going to be that add unit ID which we can get from our ad mob service which will will be defined at this point and we can just call interstitial ad unit ID on that we will have to unwrap that CU it could potentially be null then we're going to have the request which will just be an add request and finally we're going to have the ad load call back and kind of like what we did in the call back for the banner ad we're going to have a similar call back but this one we can just Define right here and for this call back we're going to be using an interstitial ad load call back within this there are two properties we are going to have to set so we have the onloaded and this is going to actually return us an interstitial ad so that will be the ad and once we have this ad we're going to update that interstitial variable that we set at the beginning to be that add and then if it fails we're just going to Simply set the intertial to null so if it does fail we will get a load ad error here which you can handle if you would like but the most important part is that we set this interstitial to null so this is going to essentially load our interstitial ad into that interstitial variable and we just need to add a semicolon here then once that's loaded we're going to want to show the interstitial ad once this button is clicked once the ask button is clicked so we can actually create another function for that to show this and then we'll call that once the button is actually clicked so this one we're going to call show interstitial ad and the first thing we're going to want to check here is if that interstitial variable is actually set so we'll check if that is not equal to null if it's not equal to null that means we'll have an interstitial ad and now we can call now we can use that interstitial and we can call the full screen content call back on it which is going to actually set that ad to take up the whole screen but we need to use the full screen content call back here and now within this we can pass pass it to different parameters we need to pass these so that we can handle two different events that happen once the ad is displayed one of those events is when the ad is dismissed and then the other one is when the ad fails to actually show so we're going to have the on ADD dismissed full screen content and this will be referring to the actual interstitial ad we'll just call that the ad there once this ad is dismissed which means the the person basically closes out the ad we want to dispose the ad so that it's no longer in the view and then we're going to actually call this method up here again to load up a new ad so at this point when we dispose this we're basically going to be done with the ad that was currently being displayed and then we're going to create a new ad here so then we'll basically have this interstitial variable reset for the next time that we need to use this this is important if you want the interstial to pop up every time without having the page to completely refresh and reload uh if you didn't call this here you won't get a second interstitial ad when you hit ask again and within this here is where you can also add some logic if you wanted to do it every every third question or every fourth question or something like that the next thing here is we need to add what we would do if the ad actually fails to load so this is going to be on ADD failed to show full screen content this will similarly have the interstitial ad as well as the ad error and you can handle the error if you would like but I'm not going to do that here we'll just ignore it in this case we'll dispose of the ad and then we will also actually call this create again so it's going to be the same thing as that as we're doing above so the last thing we need to do here is actually call show on this intertial so we're going to call show on the intertial that is now completely configured and then we're going to actually set this interstitial variable to null because we will have the ad already and it will be shown so this will just null it out and then actually when we are calling these methods again which will happen after this is actually shown it will reload another one so this might seem a little counterintuitive because we have these calls here but then we're also calling null down here but basically what you need to understand is all this code right here is configured to run when these two actions happen so we're not actually going to dispose the ad and create the new one until the ad is dismissed or the ad failed to load so down here when we actually just show it we show the ad then we null out the ad and then we basically dispose the ad and create the new intertial ad so this should be good now we just need to actually call this show on the button click and we can do that right in this function up here which is answer question this is going to be what we use when we actually click that ask button so if we call this here we should be getting the interstitial ad we're going to want to rerun the app here and now if we ask a question and you'll see we do get that full page intertial ad and then when we close this it should have triggered another interstitial to be loaded so if we ask another question you'll see we do get a second intertial ad showing up there so that is all we need to do to configure these interstitial ads you can reuse this interstitial call throughout your app if you want to have this in multiple different areas if you have a more complex op that might be useful but for this app in this example that's all we're going to be doing in this video we're going to begin creating our reward ad and what our reward ad is going to do is allow our user to get two free decisions if they watch a ad video so we're going to start back in our home View and this is just the main page here we're going to start by creating a new variable here and it's going to be for that reward ad and we'll just call that the reward and next we're going to create a helper function that we can call similar to how we have this create inter IAL ad we're going to have a new one that we're going to call in the set State here and it's going to be called create reward ad so we will now need to create this if we scroll all the way down to the bottom we can see where we have our our void functions here and it's nice to collapse these and you can see we have the interstitial and the show interstitial so right after those we'll create a new one and it will be create reward ad and this is going to Simply create a reward ad and set the state of that reward variable that we just defined to that reward ad so we're going to load up a reward ad here for the add unit ID we can use our add mob service and just call the reward add unit ID which we already set up and the request is just going to be an add request next the reward add load callback this one will be kind of similar to what we did with the interstitial ad however we will be using the reward ad load call back and there's going to be these two parameters here that we can fill out the first one on ad loaded we're going to have a reward ad available and with that ad we can actually now just set the state to that variable that we created called reward so that's what we this is what we created in the very beginning reward and we can just set it now to this ad that we'll have loaded and when the ad fails to load we're actually going to get a load ad error here and in that case we're going to just set the state of that reward to null all right and we do have just to add that exclamation point here because this reward ad unit ID could potentially be null so that looks good and now it's all formatted correctly this will create that reward ad but we also need another function to show that reward ad so let's create that right here this one we'll call Simply show rewarded ad and this is what we're actually going to use to call within the actual view so the way the re board ad is going to work is we're have a button and when that button is clicked we're going to trigger this show rewarded ad and this is going to actually pop up and show that reward at video and after that video is done playing this function here is going to be responsible for giving the user the reward so this is basically going to handle the showing of the actual ad and the giving of the actual reward so the first thing we want to do is actually make sure we have a reward ad set so we're going to check that variable reward and make sure that it's not equal to null and now we can go ahead and set the full screen content call back for this reward so this fullcreen content call back is going to set two callbacks and both of these callbacks relate to if the if the ad fails or is dismissed so the first one is on if the ad is dismissed so this one just means that the user exits out of the ad before the ad completed so we're going to dispose the ad that they exit out of and then we're going to actually create a new instance of the ad basically resetting this reward variable to a new ad so we can do that there and we're actually going to do the exact same thing for the second call back which is on the on fail to show so in this case it means in this case we will have a reward ad here as well as an error if you want to handle the error explicitly you can right within this block but I'm just going to do the exact same thing for this and dispose the ad that failed to show and then basically instantiate a new ad there so now this call back is set up so basically the ad itself is ready to be shown to the user so so again this is going to be called when the user clicks the button to see the actual reward ad so the first thing we want to do is show them that ad so to do that we'll just call show on the ad and the show has a call back method here which is on user earned reward So within this we can actually give the user the reward that they would get from viewing this ad so we're going to have access here to the reward ad and the reward item so this reward item will actually give us some information that we configured an admob about our reward ad itself however if you are only really using one reward ad in your app such as this example you will know that the reward ad will always give the same reward so you won't really need to necessarily check that and you could essentially just put what the reward will be right in here let me hardcode it now and then we come back and I'll show you how to use that reward item there so the reward for watching this ad is going to be that the user will get two free decisions added to their decision Bank up here so if you remember the way we structured this in our model here we do have a bank option on the user's account and that bank is what we use up here for the number of decisions that they have left so we just need to increase those decisions by two and we could write that code right directly in here to increase the decisions but it would be better to make this just a separate function that we can reuse later if we need to to increase those decisions we'll call this increase decision and it will take one parameter which will be the quantity and that quantity will be how many decisions to increase this by really all we're going to be doing is updating one parameter that bank parameter in fir store so so first we can set a new variable for the bank and this will be a this will be the new bank value and we'll set that equal to the current bank value which we can get through widget account bank and then we're going to add that quantity that is passed into it so if the original value is eight and we pass in two now it will be 10 and this value here will be 10 so all we need to do is update our Firebase instance here and update that collection which will be the users collection and then we need to find the document for that user which we can get from their uid which is going to be stored on that account and finally we can call update and all we want to do is update the bank so we're going to just write that here and the value that we're going to set it to is this new bank value so all this will do when called increase decision with a quantity is update that user's bank number to essentially add that quantity to it now we can call increase decision down here and we can pass in the two so that will give them two new decisions all right that should be good and that is going to be it for this video in the next video we're actually going to add that button and then start calling this method from that button when it's clicked so in the last video we set up the two functions one to create the reward ad and the other to show the reward ad this video we're going to set up the view to actually have a button that you can click that will show this reward ad and actually give the user the reward so the first thing we'll do is create a new widget and we're going to call this build reward prompt so initially let's just return a button and this will be an elevated button when this button is press we are going to actually call that show reward ad that we created in the last video so we can put that right here and the child here will just simply be text and the text will say get to free decisions all right so now we can just place this in the Bild build widget so we want this right above the should I which is the question form so if we add this right here we can actually add a spacer below it as well and if we save this you'll see we now have a button that says get two free decisions and if we click that button it actually does not work but that's all right that's semi expected at this point there are a few more things we need to add first firstly we actually don't want this button to display at all if there is no reward available so this reward is getting set down here when we create the reward ad which is being called up at the top in our did change dependencies but initially that reward variable is going to be null and we aren't going to have an actual ad set so we want to make sure that the reward exists before we even show this button so we can do this with a simple check to see if that reward equals null and if it does equal null then we're going to just return a container and if not then we'll return the button so we're going to want to rerun our app so at this point you can see there's a bit more of a delay before the reward is available and now if you click it you'll see a reward ad actually does pop up so before that wasn't happening because the reward wasn't available when we were putting in the widget tree but either way that looks good now and you can see we were rewarded we had eight before now we have 10 so that looks good but there are a couple more things that we want to add here when the reward was removed we would like this to kind of show that you were rewarded those extra those extra points and not necessarily just have this immediately right there so we can configure that by adding another parameter and this is just going to be called show reward and this will be a bull which will by default be false so this show reward is going to mean that we want to show the user the reward that they just got the build reward prompter down here we're going to change this logic up a bit and check if the reward is no and if the show reward is true then we're going to to change this container to a column and within here we're going to add an icon and we're also going to add a text so the icon is going to be this plus two icon right here and we're just going to set the size of this to be a bit larger and the color to be orange then right below the icon we're going to have some text and it's just going to say you received two new decisions and we'll just add a little bit of style there so that will be good now we're going to just actually wrap this icon in a bit of padding and that will be good so this is going to show if the reward is null and if the show reward is true that is never going to be the case right now because we aren't setting this show reward equal to true so originally we were checking if just the reward was null and returning that container and we're no longer doing that so we need to add that logic back in so we need that to be below this because this is checking if the reward is null and the show reward is true so we can add an else if here if just the reward is null and if that is the case then we're going to return that container and while this is okay and this does work I don't typically like to use the else block for something that actually has value so this has much more value this button than this container does so I think it's better to switch these because if the reward is not null then we'll show this button but if this condition isn't met or this condition up here then by default we want that else to just catch the most basic least valuable thing so it'll just return a container so this is good we basically have this now but we don't have any logic setup to actually ever make this true so we need to set that up now and that's going to happen down here where we actually show our reward ad so after we show the reward ad we can set the state of those two variables and the first one is the reward which we want to set to null here because essentially the ad is no longer needed and we can recreate that ad later by calling create reward ad again but after seeing the ad initially we want to set that to null and then we also want to set that show reward equal to true so this will show the user the reward so if we rerun this and add another reward here you can see we have our reward and then when we close out of it it did show that there was a reward but then our call back up here which is called on dismiss actually kicked in and created a new reward ad that is okay though if you wanted to make that display for longer you can either add a time offset here or you can decide that you don't want to reload the option for another reward ad and if that was the case you could just not call this again and just instead just dispose of the ad but I do like that this pops up again and the user has that option to again watch another video and get more free questions so that is going to be it for this video one last time if you can see if the user clicks this they can view that reward ad and then when they close out of the reward ad they're going to be shown that they get those two free decisions and their bank is updated and then they'll have the option to continually watch these if they want so one other thing to note here with these reward ads is you can actually use the reward ads information that you set in admob for the reward so I'm going to print this out and you can decide if you would like to use that or use the hardcoded value here there is a little bit of a benefit to using the ad mobs data because you do have more control over changing it without creating a whole new release so you'll see there's two actual parameters really it's the amount and the type so if we save this and run it again we are using test ad unit ID so it is always going to be the test information but I'll show you where that's set in ad mob if you actually get the reward here you can see that the reward information and we get 10 for the amount and then coins for the type so that amount and type is actually of values that are set in admob so if you go back to admob and you find your reward that you that we set up earlier you can see we have our reward settings here and for me the reward settings the amount is two and the the item name which would be the type is decisions so if I switched from using a test unit ID to using my actual ad unit ID this is the information I would expect to see there so you can imagine this would be nice if you want to change it and you decide one day you want to give people three instead of just two and you can just save it there and then when you come down in here if you were using the reward amount instead of this hardcoded 2 that's really all you would need to change is what you CH is what we just changed in admob because this is already going to increase the decision by that reward amount although there is an error because this amount is a double and we would need to make that an integer so if we just change that to int there and rerun the app and I had a also go into the database and just zero that out because we saved it as a double so anyway if you use 2 in there this should give us two free decisions here and you can see it did give us those or it actually give us 10 free decisions this time and that is because we're using that test ad unit ID and it does have 10 set for the unit so that is expected so the only downside with this is that we don't really have access to that value of 10 in the reward item until we show the reward so because of that we aren't able to update what the button text says here and I feel like this is something that will probably get added to this package eventually seems like we definitely should be able to get that amount without having to show the reward but in any case instead of this instead of this saying get two free decisions it could just say get free decisions or you can hardcode it as it was before I'm just going to leave mine hard-coded for now but I thought it would be worth showing you that you can use this reward item information one other very small thing that you might notice is when you click this button before the reward is fully popped up you can already see the text that says you got the two free decisions if you look really quickly this can easily be changed by moving this set State here into the actual on user earned reward call back so we'll set the state there and now when you run it if you save this and run it you'll get the reward that time you notice it did not pop up but once we click this x it's going to be there in this video we're going to add native ads to our app so a native ad will allow you to create a template for your ad using the native device code so for iOS we can write this in Swift or objective c in this example we'll be using Swift and then for Android you can use either Java or cotlin and basically what you're going to do is create a template for the ad that then admob will use and fill in that template with the actual ad code so kind of how we have our Banner ad down here we're going to create a new native ad at the top of our list view up here and it's just going to be for example purposes to show you that you can make a custom card that is an ad all of this code and setup is going to be heavily based off of the code lab for adding admob banner and Native inline ads to a flutter app and I'll link this down below and we will be taking the custom cotlin and Swift Code template that this tutorial is using here but note that if you wanted to create a native ad in your own app you can change this around however you like you will have to write it in the native code so if you're doing this foros you will have to learn a little bit of Swift and update that but if you wanted this image for instance to be larger in the center and then have the text below it you can definitely do that with these native ads and once you get this set up it's pretty easy to change out those templates or modify the ones that come from here so the first thing we need to do is actually write that native code so if we open up our iOS folder here and click on any of these files you'll see in Android Studio that we have this option to open the iOS module in xcode and once we get that opened up we're going to go into this Runner tab here and this is where we're going to actually get and drop the file that has that custom native ad so if we look at the complete code from that example we can dig into the complete iOS example and then go to the runner and we're looking for the list title native ad view so we're going to open this up and then just go ahead and copy the raw content back over in xcode we can add a new file and this will be a view so this will be under the user interface and then we're going to select View and then hit next and we're going to title this the same as that other tutorial is using so this will be called the list tile native ad View and once that loads up we're going to have this blank empty view here so if you rightclick on the file and then open as source code you'll be able to actually just edit all the content and paste in the content we just copied from GitHub so you can read through this a little bit but it's mostly useful to just look at the interface Builder now and you'll see exactly what we just got so we have an image file here and then we have a label and another label so pretty simple but this is what the ad template will look like for these Swift native ads we now need to create a new class that will link up these three elements here and essentially map them to the three elements in the ad that we want to use so this will use the image this will use the headline and this will use the body so we're going to create another new file over here this time it will just be a swift file and we will call this the list tile native ads Factory and then go ahead and hit create so the first thing we'll do is import the Google mobile ads and we already will have access to this because our project is already configured with that it is a bit strange coding in the this way cuz we're using xcode but we are actually still in the same flutter project we're just working directly out of that iOS folder and now I'm going to paste in the code from that example and then I'll just explain it so this is going to create a function called create native ad and really what this is going to be doing is mapping these native ad elements to the view that we just created so again this is the list tile native ad view you can see that has those three elements and right within here we're creating a reference ref to that view and once we have the reference to this native ads view we can map the headline body and icon to a native ads headline body and icon and this native ad is what is going to be passed in so you could imagine a ad is passed into this function and then we're telling that function use the headline and map it to the headline in our view right here and do the same thing with the body and the icon and then finally this will just return that view and the view will already have the data filled out within it the final thing we have to do is go into our app delegate here and really just add two lines that are going to allow this to override the Google mobile ads plugin and that is just going to allow us to use this within our flutter code firstly we will have to import the Google mobile ads and then down here right before we return we need to add this line so what this is doing is getting us an instance of that list title native ad Factory which is the file we just created here so it's getting us an instance of this class and then it's configuring our Google mobile ads plugin with a native ads Factory which will be this actual native ads Factory so now our Google mobile ads plugin will have access to a native ad in the flutter code and it is going to be using this Factory ID here so if you want to make multiple different types of native ads you can use different Factory IDs and basically just create multiple different classes of the Native ad Factory and map these however you like and they can map to completely different views in the flutter code you'll see we're actually going to use this list tile ID and that is going to be how the Google mobile ads plugin knows to look up this specific native ad code so this is all we're going to need to do in xcode we can go back into our Android studio now and update this to work with Native ads we'll get it running first on iOS and then we'll go back and configure the Android version so we can close out of this pod file and open up our history view so now within the history view we can define a native ad and this can be a late variable and we're going to configure all of this in the override of theit state and the first thing we want to check here is that the user is on a free plan so if you look down where Banner ad is being called we already have that check right there so we can copy that up here so we're only going to initialize these native ads if the users account type is ad free false which means that they should be seeing ads and now we can just Define this native ad so it's going to equal a native ad and there are a few parameters we need to give it now so the first is going to be the add unit ID which we have not yet configured we can go ahead and create these add unit IDs by clicking on ADD unit and adding a new ad unit ID this will be the native ad and we'll just call this native and then we'll get this add unit ID here back over in our flutter code we could paste this here but if you remember we have that nice service class for our admob service and we already are setting these up better here to use our test ones and also know if it's Android or iOS so exactly the same how we're doing that for these the logic is the same but we will change this and do it for our native ad unit IDs and I'll just go ahead and copy that native one again this was for iOS and now I could create one for Android as well that is good and I also need to update the test ad unit IDs for Android and iOS and I'll put a link down below where you can find these but now we have this native ad unit ID that we can pull from our admob service so back within our init State here we can get this off of the provider by calling context read next we're going to pass the factory ID and as I was just describing that factory ID is going to be whatever Factory ID we give this right here so we're going to want to make sure that this this is the same for IOS and Android but we will be calling this list tile next we'll just have the request which is simply an ad request and lastly we're going to have a listener so similar to the other AD listener that we already set up for the banner ads you can see there's a few things that you can pass in these you can pass in for the native ad as well we're only really interested right now in the on load and on failed to load so we're going to copy these two over and just create this listener Direct right here as a native ad listener and we can paste those two options in so on ad loaded we're actually going to change this to set a variable to true so that we know that the ad is loaded and then we'll later use that variable to either show or not show the ad so we're going to be setting the state here of a variable but we do need to create that variable and it will just be a true false value this will be called the native ad loaded and to start this will be false so once the native ad actually loads we can set this equal to True lastly if it fails to load we are just going to dispose the ad so we could do this all in one line and just remove the rest of that so now we have this native ad and we just need to finally call load on it so now when our page is initially viewed we're going to be loading up this native ad and then setting this variable to true once that ad is loaded so we can go ahead and use this now to display it in our actual view so we're going to display our ad right above this list so we can wrap our list in a new column and now we can have a element here but we're only going to want to show this if it's loaded and if the user should see ads so first we're going to just see if the native ad is loaded so that should be true if it is loaded and if that's true and then also the user's account is not add free so if add free equals false then we will show them the ad and to show them the ad it's pretty simple we already have it loaded we just need to create a container for it and in this container we'll have a ad wiget which takes one parameter and that is that native ad since we defin this to be potentially no we will have to unwrap that there and then we can set a height to this which will just be 50 but depending on the native ad style that you create this might be larger and lastly we can set an alignment to be Center and don't forget to add a comma there this list view will now need to be wrapped in an expanded widget so we can add another widget expanded all right so this should be good but since we made all these changes to the native code we should run a flirt clean and then we can rerun on our app all right and once our app reloads we can go into the history view you can see at first the native ad was not there then once it loaded it did pop up so that's as we would expect and you can scroll through this and it will just remain at the top there because we did place it above our list view Builder but yeah all this is more just of an example of how you can make these native ads and depending on how you want to use them you can customize them really however you like if you want the image to be more prominent you can make the image take up a large chunk of the screen and all of that can be done by just editing that native template so this isn't currently going to work right now for our Android device and that's because we need to actually create that native code template for Android as well so we can go ahead and close out of this simulator and open up an Android one so testing on Android here if we go into the history view you'll see everything works except that native ad and if you do look in the logs here you can see that we do get a platform exception that that the native ad error can't find the native ad Factory with ID list title and this is all expected we don't have a native ad Factory for called list title for Android so we do just need to create that so the first thing we can do is open this for editing in Android Studio even though we're already in Android Studio this will just open up the Android project code itself that is related to the flutter project it's kind of the same thing that we just did with iOS except now we're doing it for the Android code so now that that's open it should look like this and we could open up into this app here and we're going to create a new Android resource file so we could right click here and then choose new Android resource file this is going to be the file for that view template we will just name this the list tile native ads. XML and for the resource type it is going to be of type layout and then now we need to Define the root element which is going to be a native ad view so that is going to look like com. google. android. gm. ads. native ad. Native ad View and we can keep this in the directory of layouts and then click okay so if you wanted to build this yourself you could go ahead and build that out but I'm going to use that code example from that tutorial so we can clear all of this out and we can just go over and get the list tile native at XML this is going to be quite deep ly in a bunch of folders but you can find this code here and copy it if you like and really this is doing basically the same thing that iOS one did and it's just going to have an image and the headline and the body so now we need to create that native ad Factory and link up our Google mobile ads to fill in the appropriate data in this template so we can ex outed the template actually and we can actually find now the Java folder here and then find our project this is actually our app project ID and if you look in here you should have a main activity file already and we'll be updating this in a second the main activity file here is kind of similar to the app delegate in the iOS version but we do need to create that list tile native ad Factory so we'll create a new just cotland file and this will be a class which we can call that list tile native ad Factory once we've created that we can actually just go and find that code again over here and this one is going to be quite a bit further in but this is the code we're interested in we do not want to include the package name because we already will have our package name there but everything else we can essentially take and this is going to do the same thing that we saw in the iOS version it's just going to be getting a reference to that new view that we created and then populating it with the native ad data that is going to get passed into this so once we save that we can actually now go into our main activity here and what we need to do now is add an override function that is going to register that native ad Factory so I'll drop that code in and briefly explain it so we will need to import these and if you highlight them you can probably just import them directly but again this is going to do very much the same thing that we did in iOS it's just going to register with our Google mobile ads plugin in that we have this new Factory called list tile and then we're going to go and get that list tile class Factory that we already set up that is going to link it then to the native ads template with Android there is a second one for cleaning up this app engine and it's basically the opposite of this instead of registering the native ad Factory we are just going to be cleaning up the registered Factory so essentially the same thing we're just going to unregister that native ad Factory by that factory ID and really at this point that is all we need to do to set up this native ad so if we go back into our Android Studio that is running our flutter app we're going to stop the app and also run a flutter clean and there's actually one last step that we will need to do here before rerunning this and that is going to be updating one line in our Android app SRC main Android manifest and what we want to do here right within this activity is Mark that Android exported is true this is going to be required to allow us to access that native code so now if we rerun the app we can go into the history View and we now see that we get that native ad right there as we would expect just like we got it for iOS so this is great we now have native ads running in our app for both IOS and Android and I will link that tutorial down below because it does have a lot of this boiler plate code that lets you get this set up I didn't really think it would be worth going through line by line how this code works because I do think that tutorial is pretty well written but if there are further things you want to see with Native ads that aren't covered in that tutorial and you can't figure out by watching this definitely let me know in the comments of this video and if there is enough people interested in seeing more stuff on Native ads I could definitely make another video about Native ads and add it to this course but I think this will pretty much get you everything you need to actually add those native ads yourself in this section we're going to set up all of our inapp purchases so we're going to start by setting up our consumable inapp purchase which is going to allow someone to buy a fixed amount of decisions so they're going to be able to buy five decisions 50 decisions or 500 decisions what makes these consumable is that the user is going to buy these five decisions and then they're going to use the five decisions and once they've used them that's it they're not going to get five more decisions if they log into a different device they're not going to get an additional five decisions just because they're on a different device they'll get the five decisions and they'll be able to use them whenever they want but once they're used they're gone then we'll set up the non-consumable ad which is going to give the user premium access to the app and What premium access means is an adree experience so if they buy this non-consumable it's a one-time purchase and once it's purchased it's going to be always on their account and they're never going to see any of the ads that we set up in the last section use us a non-consumable to remove ads is one example but you can use it for various other things in your app that a user would buy once and have forever after that we're going to set up an unlimited subscription which is going to have two options either monthly or yearly and what the unlimited subscription is going to do is give the user unlimited decision so they can ask any number of questions and then it's also going to give them the same features as premiums so they won't see any ads in the app all of these Concepts should pretty easily translate to your app and allow you to have the same type of purchase structure structure but offer different features within the app so for instance the premium and unlimited user types meaning if they bought the non-consumable premium or the subscription unlimited that's going to be saved to their user object and once a user has that specific object you can use that anywhere in the app to check for certain things so if they're unlimited you can show them whole new pages or whole new routes and give them access to whole new features in the app that aren't just necessarily the decision based stuff so really the concept of getting someone's user type marked in one of these ways is going to be what you'll be able to use in any app that you build and monetize so let's get started with our inapp purchases the first step to setting up Ina purchases in an iOS app through flutter is actually to create the app in app store connect so this video we're going to be going over how to do that this is going to require a developer license so if you don't have that you will need to go pay for the Apple developer license so you have access to all this stuff you're not going to be able to configure in our purchases without the developer license so you're going to go to App Store connect.app ale.com and you'll be logged in with your developer license and you're going to go to my apps here and we're going to be clicking this plus button here to add a new app and our platform will be iOS the name will be decider and I'll add that y to at the end to keep it consistent with the rest of the course material here the primary language for me will be English us and now it's going to ask for the bundle ID so if you look in the drop down here the bundle ID that I'm looking for is actually not in there so to find the bundle ID that you need if you open your app in xcode and click on the runner General you can see right here this is the bundle identifier that we are after so we are going to need to create that new bundle ID we're going to do this through our account here and then click on the certificates identifiers and profiles here and you'll notice right here there's no identifier for the app that we're after because it is getting included with this wild card so we're going to click the plus there and we're going to add app IDs and hit continue this will be for an app hit continue again the description here you can put the decider YT again and a lot of these start with XC for xcode that's because they're they used to be generated by xcode but anyway now we can go into xcode get this bundle identifier here and paste it right in there and now it's going to also ask down here for any of the capabilities we will configure these later so we can just hit continue for now and then we can just hit register so you'll see now in this list we have that decider YT that we need there if we go back to this tab we are going to likely need to refresh this just to get that updated but now if we click on new app and go to bundle identifier you can see our decider YT is there and I'll just fill back in the rest of this information then for the skew this will just be any unique ID you can write here and we're going to want to give the user access full access and then go ahead and hit create great so this brings us into this page so we have the app now set up in app store connect the next thing we're going to do is add the inapp purchase signing capabilities to this app and this can be done through xcode so we're going to go back to xcode here and we're going to click on signing and capabilities here and we're going to add a capability we're going to just search for the inapp purchases and double click that so that is going to add that inapp purchase signing capability and you might have noticed it updated that signing certificate for us there final thing we're going to do is create the inapp purchases in the App Store connect and this is going to actually get us some IDs that we can then later use in the code so in our app here we're going to go down to the inapp purchases and click manage we're going to create all the different types we're going to be using which there's three different types and there'll be a total of six different Ina purchases so firstly we can create the consumable and there's going to be three different ones of these the consumable is where you basically can use the in purchase and then once it's used it's gone so this will be for new decisions and it'll be basically just three different denominations of decisions so the first one will be 4 five we'll call this decisions five for the product ID this needs to be something that's unique across your whole account so this will name decisions yt5 it's important to make sure that this is all lower case and only using numbers and underscores the reason for this is we're going to want this to match up exactly with the product IDs we have in Google Play and unfortunately Google Play does not allow capital letters so make sure these are all lowercase here uh next we can choose a price this one will be 9 and lastly we're going to need the display name and description so for display we will put five decisions this is actually going to show up in the app so keep that in mind but you can always change it later and for the description it'll be get five decisions to use any time later on we can talk about the App Store promotions and we will be using this review when we actually go and submit the app but for now you can just go ahead and save this and that is good so we will have this one consumable here I'm going to go ahead and do the same exact thing but do it for 50 decisions and 500 decisions all right so the three consumables are now complete the price the only thing that's different really is the price and then obviously the number so the 50 is priced at $4.99 and the 500 is priced at $9.99 next we can create our non-consumable Ina purchase and this one's going to be used for moving to a premium user type and what we're going to give the user if they purchase this is an ad free experience if they buy this once they won't have to see any ads in the app ever again so we can go ahead and add this as the non-consumable we'll name this premium and for the product ID it will just be premium YT this one we're going to price at $5.99 and we will just need a display and description so display is just going to be premium and the description is going to be an ad free experience the rest we can just leave as it is and just go ahead and hit save so the last type of inapp purchase we're going to have is the subscription and we're going to have two different subscription options one for monthly and one for yearly but essentially they're the same subscription just different time periods the subscription is going to give the user an unlimited amount of decisions as well as the adree experience so it'll be essentially like premium and unlimited decisions so we can create this with an auto renewable subscription and we'll do the monthly one first so we'll call this unlimited monthly and the product ID here is going to be unlimited YT monthly next we'll have to add a subscription group so I'll create a new one just to show you what that's like unlimited access is what we can call our subscription group and I'll add the YT at the end of that the subscription group is basically going to prevent someone from purchasing two of the two of the same or similar subscriptions so if you think of the case of the monthly and the yearly we wouldn't want someone to be paying the monthly fee as well as the yearly fee for this because it's really the same subscription it's just a different duration within the unlimited monthly here we can choose a duration of one month and then we're going to add the pricing which for the one month will charge $1.99 this isn't going to allow you to change the pricing per location but I'm just going to leave it all at the default and hit create then we can go ahead and save that pricing there's also options for introductory offers and promotional code you could set those up if you like but I am not going to do that for my subscriptions lastly we will need to add a localization so we're going to do this for English us and this is where we will add the display name and description for the display name it will be unlimited monthly or unlimited month rather and then for the description it's going to be unlimited decisions for a month with no ads and we can go ahead and save this I'll go ahead and do essentially the same thing for for the yearly subscription and the only thing I'll change is the price which will be $19.99 all right so now we're all set and we have our six different inapp purchases that our app is going to support there is one last thing we need to do within the subscription groups and if you find the group that we created which is the one that's going to have the two inapp purchases this other one is just a test that I had so go into the group that we're actually using and within here you can see the two different types of subscriptions that we have we do need to add a localization for this the localizations you need at least one but you can add one for each different location we need to name this group so it's just going to be called unlimited access and we can use a custom name here of just decider so go ahead and save that and this is good now our app store connect is all configured for and our purchases the most important thing here that we're going to be needing in the code later is those product IDs so these product IDs right here we will be using in the codebase which is why we want to have these created before we start writing any of that code the first step to getting na apppp purchases on the Android app is also going to be creating that app in the Google Play console so this is going to be very similar to what we did with the App Store connect and we're going to be creating that app and then we're also going to be creating those inapp purchases so this is also going to require you to have a Google Play merchant account and that is similar to your Apple developer license the Google one is a little bit cheaper but you will need that in order to get your inapp purchases completely set up so in our Google Play console here we're going to create an app and we're going to name this decider YT default language is the same the this is going to be an app not a game this right here is asking if we want to actually charge for the app so the app itself is always going to be free but we're going to have the inapp purchases in it as kind of add-ons if you did want to charge for your app this is where you would do that then you're going to need to read through these developer program policies so go ahead and check out all of those and confirm if it is something that you have read through and agree with and also the US export laws that can be all found here so then you're going to go ahead and create app there there will be a bit of configuration that we're going to do later before we actually can get this live but for now we can just create those inapp purchases so if we scroll down here to the monetization or to the monetize tab here we can go to inapp products and you'll see that our app needs to actually have some permissions in our APK so we will need to upload an APK with those permissions first and then we'll be able to configure those and app purchases within here so we can create that APK with the billing permission in Android Studio we're going to first go to the terminal here and we need to generate a key store and this will just allow us to sign the app there is a command here that I'll link below but I'll also just type it out so it's key tool dasg key- v-key store and now we're going to name the key so we can call this decider YT and it would be a jks file Das key ALG and that is going to be RS a dash key size is going to be 2048 Dash validity and that is going to be 10,000 and lastly we can do a dash Alias and we'll just Alias this as key this is going to walk us through a couple steps so we're going to enter a key Store password for this example ex Le I will just use password although you should not use that it'll ask you your first and last name these are really just to make the key more secure so you can really put anything for any of these and lastly It'll ask if this is correct I actually incorrectly inputed this so I'm just going to update that and now it will generate this key and it will store it right at this path here so the key is named by this but remember that we did Alias it with the word key so once the key is generated we're actually going to store the values of the key in a file here so open up the Android file and we're going to create a new file and we're going to name this the keyproperties and we're going to Define just four parameters here so we're going to have the store password and that's going to be the password that we typed in down here when we were configuring this so I used password the key password is the same then we have the key Alias and that is if you remember key and lastly we have the store file and that is going to be this right here and just remove the space so now that we have the keyproperties file set up we can actually use it to sign our app so so we're going to be in that Android folder and then go to app and then build.gradle then within this file here we're going to look for this Android block and just above it we're going to define a new variable for our key store properties and basically what we're going to be doing here is using all these values here but creating them as a properties object so we can Define our key store properties as a new property object then we're going to need get our file so we're going to take the key store properties file and that we just need to get a reference to that file we just created so to do that we can use the root project path and then call file on it and look for that file which is called keyproperties finally we can check that this file exists and if it does we can take this key store properties object and load the information from the file so we can do that with a file input stream and just pass in the file so all that this is doing is setting this key store properties value here to essentially the values within this Key Properties file so we'll be able to use this down below to configure our signing of the app so we're going to look where this default config is and just below it we're going to create a new block for the signing config and that will be signing configs with an S then we're going to define the block for release the release is really all we're interested in this that's what we'll be building this in when we're going to be submitting it to Google Play typically we'll be building it in debug mode or development mode So within here there are four lines we need to add and they do line up with these four lines here so the first thing we need to do is set the key Alias and that is going to equal our key store property of the key Alias the next one is going to be our key password and very similar then for our store file since this is actually a file it will be a little bit different we're going to get the key store property of that store file and then assuming that exists which is what this question mark will do we're going to get the file at that route and if if it doesn't exist then we can just make this null um but it should always exist assuming that configuration stays the same lastly we'll have our store password and that will just be the key store properties of our store password all right so we can save that the last thing we're going to do here is update the build types for our release and we can actually just remove this comment here and we're going to change debug here to release and go ahead and save that so our app is now configured to be signed but we do need to add that permission so the whole purpose of this is that we need to add a permission but we won't be able to create the APK without signing the app so that's why we had to do all that but now we can add that permission which is actually quite simple we will actually need to initialize the app with our inapp purchase package and then update the APK on Google Play the package we're going to be using is called inapp purchases and it is at version 1.0.9 at the time of recording this so that is the version we'll be using you can copy this line here to add it to your pubspy file and if you run that and check the pspec doyo you'll see you get a version here of 1.0.9 we can additionally run that Pub get and now the package will be available next we're going to go to the main. dart and there is one line we will want to add now if we are using Android you can see this in the documentation here if you go to the read me the line we're going to be wanting to add here is checking the default Target platform and if it is Android we're going to enable this pending purchases and what this is going to do is allow the app to have that billing set up so we're also going to need to import these two packages so we can just copy these over as well and if we save this that should all be good and we'll be able to now create a build of our app and upload it to the Google Play Store so I've already been testing this a few times so I do need to go into my pspec yaml and increase the version here to four if this is the first time you're creating the build you won't need to change that but every new build you make you do need to increase this so that they are unique the next thing we'll do is build the app bundle so we can use the command flutter build app bundle and while that's running we can look in our Google Play console if we go back down to the Ina purchase Tab and click the upload new APK we can see we have the alpha track here and we're going to want to manage this track and then we're going to create a new release on it and this new release is going to be taking the upload that we're generating right now and that build just completed so if we navigate to this file right here which is under build app output bundle release and then we have that app release. aab we can take this file if we reveal and finder we'll get the file here and we can now bring that over to our Google Play and drop it in there so this release now has the app configured with the Google billing permissions so it should allow us to create the inapp purchases now on this app once that finishes uploading we can save this and then we can review the release and all of this stuff looks okay here if you have any errors up here that you need to handle then you might have to go do that but then you can start the roll out to Alpha and roll it out so great you'll see in our releases here and it is in review but that's all right since we configured this with the billing we will be all set now to create our inapp products so if we go over to monetize and inapp products over here you'll see now we are able to create products all right so now that we have a release on Google Play that has the billing information included through that inapp purchase package we can actually go ahead and create our inapp purchases So within our app here we want to go to the monetize section and then click Ina products and you'll see now we do have the ability to create a product so if we go ahead and click that we want to create these products with the same product IDs that we used within our App Store connect so we want our five decisions consumable product in Google Play to also have the same product ID decisions yt5 the reason for this is it's going to make the code base a lot simpler because we're going to be able to always reference that same product ID regardless of the app type uh it is possible to have them separate if need be but ideally you want to make these the same product ID so we can copy this over and we can also copy over the display name and description for pricing we're going to set the price to 99 so it will be the same price as the iOS version and we can go ahead and save that once it's saved we can activate it and now we will have this activated in at purchase I'm going to go ahead and do the same thing for the other two consumables we can also create our premium subscription here the premium YT and that will be named premium the Google Play Store doesn't make you determine if it's a consumable or non-consumable within here so we create these essentially the same so this looks good we have our three consumables and then our one non-consumable here for the subscription we will actually create those over on this subscriptions Tab and we'll start with by creating subscription here the product ID again we do want to match up to what's in iOS so we'll start with the monthly the name will'll give unlimited month and then the description will be the unlimited decisions for a month with no ads benefits I won't be adding but you essentially can add different benefits up to four I believe just telling the user what the benefit are of the subscription pricing for this one will be the monthly and we're going to set that price to $1.99 I'll leave all of these as the defaults and go ahead and hit save and then activate I'll just go ahead and create the other one for the yearly which is going to be essentially the same thing with just a changed price and change duration all right and that looks good we now have our two subscriptions to the monthly and the yearly and then we also have our other four inapp purchases the consumables and the one non-consumable so this is all good we now have product IDs for both IOS and Android that match up and link to the same type of inapp purchase so at this point both of our stores are configured for Ina purchases and the next step is actually to set up our app to use them now that we have our inapp purchases set up in Google Play and the App Store connect we can begin setting it up in our app to retrieve those inapp purchases and display it to the user so we're going to create a new view and this will be in the views here and we'll call this the store view this will be a stateful widget and we will have to import material in the build here we're going to return a scaffold and we can first give it an app bar which will simply have the title as text which will just say store because this will essentially be the store where users can buy any of our inapp purchases next for the body we're going to start with a safe area and then we'll have a column which we can fill with various elements uh for right now let's just put a text element that says welcome now that we have a very simple store view set up we can link this icon here to actually display it so that actually is going to be in our home View and if we go into our home view we also have a scaffold in here which has an app bar which then has two actions and one of the actions which is the shopping bag icon has just an empty on Tapped action so we can update that to navigate to our new store view so we're going to do a navigator of build context and we're going to go ahead and push the route and the route here is going to be a material page route and for the Builder we are going to give it that store View and this should just be pushed not pushed named so if we save this and push that shopping bag icon you'll see we do go to that store View and it just says welcome as we would expect so now we can set it up so that we can actually start pulling in those inapp purchases so back in the store view we are going to have the very top here Define the Ina purchases that we are going to allow on this page and this is going to be a constant of type list and it will just be a list of strings and we're going to call this list the product IDs the product IDs are going to be those IDs of the inapp purchases we set up in app store connect as well as Google play so it's going to be these IDs here so we're going to want to copy these over as items in this list here so these are all of our product IDs but to make things a little simpler initially we're going to only include the three consumables we'll come back and do the non-consumable and the subscription so the next thing we're going to do is request these inapp purchases based on these product IDs from the store that we have already configured them in so we will be doing this on iOS and we're going to essentially be trying to get these and our purchases from App Store connect since this is going to be making a call to App Store connect or Google Play We will be using a future to get this data So within our store view State here we're going to create a new future and it's actually not going to return anything it's just going to be setting the states of different variables and we're going to call this nit store info and that will be an async function and now the first thing we want to do is see if our store is available so to do that we are going need to create an instance of the inapp purchase package so we can do this up here as a [Music] final and it will be of type inapp purchase and we can just call this inapp purchase and then it's just going to be an instance of that inapp purchase now in our future here we can use this instance of inapp purchase and we can call on it is available and this is simply just going to tell us if the store is available so we will need to set this as a variable and it will be a bullly in value and we're just going to call this is available this call will need to be awaited as well so we'll have to add that in there and now we're going to set the state of a new variable that we'll also create up here and it will be our is available variable originally this variable is just going to equal false and I do have a typo there so we'll set this variable equal to the value that we get back from this call uh this might seem slightly strange to you in that we're using kind of the same variable twice and why not just set this directly and the reason for that is that is available we don't want to be a future value we just always want to be able to use it throughout the app this is not a future however the value that is available in this context is set to needs to await the call to the inapp purchase instance so basically what all this is doing is just getting this value and then setting it as this variable that then we can continue to reuse without having to worry about if it's set or if it's still awaiting that call to complete so now that we have this is available here we can continue to use it here and we can see if it is not available so this would mean that the App Store is not available and the page is not ever going to show any ads if this is the case if that's the case we want to put a note on the page that basically says this is not available so we're create another variable here and call the notice and this we're just going to say there are no upgrades at this time notice will need to be defined as well up here so this is going to be an optional string uh which just means it could be null if the store is not available we're also just going to return here because there's no reason to try and continue and get those inap purchases if the store is not even available so after this we will get the inap purchases let's go ahead and use this notice within the body and see if the store is available instead of having this text here with welcome we can actually have a quick if statement that says if the notice is not null so if there is something in that notice then we're going to add a text that just has that notice text in it and we can also wrap this in padding and also we need that exclamation point there so if we save this you'll notice nothing really happened but that is kind of expected there's a few things we do need to update firstly we need to wrap this notice in a set State and actually set the state of that notice there we're also going to set the state of this if it gets down below here which just means there is a connection so this is kind of temporary for now but it will be good to see that we are actually connected to the store and lastly we never actually called this a nit store info so right above here we can call this in the AIT state so now if we rerun this you will have to go back a page and then go back into here and you'll see there is a connection to the store so this is good we do have that connection to the store available in the next video we're going to actually call the store to get those inop purchases so continuing on we're going to retrieve the inop purchases from the store that we have so we can get all of the products and all of their details from the instance that we have of inapp purchase this is going to return us a product detail response and we can call this the product detail response and this will be returned by a call which is going to be asynchronous to that inapp purchase instance we're going to be querying for the product details so we need to pass in that identifiers here which we already defined up above which is this product IDs so we can copy that and we need need to convert this to a set so we can just call two set on it and we can end that with a semicolon so this is going to get us our product detail response here so we can use this product details response now to set a variable of the list of products so we're going to Define another variable up here and this will be a list of our products and it will be of type product details so it will be a list of the product details for all of our Ina purchases and we'll call this the products and initially it'll just be an empty list so down here we're going to Simply set the state of that products list and it's going to equal the product detail response and then we're going to call the product details on it because you can see that the product details here are the list of product details from that response so that should be good and after this we can also just print out the products here so currently we would expect to get three products back cuz we only have our three products right here that we're passing the product IDs for so if we save this and rerun you can see we do get three instances here of those App Store product details so that is good there are two potential issues that we do want to handle though and we'll handle those with different notices so the first one is going to be if this product details response here contains an error and we can test that by calling error on it and we want to see if this is not null so that would mean that there is actually an error then we're going to set the state of this notice to equal there was a problem connecting to the store then we can add an else if here and this one we want to check if the product details response. product details is empty and there is already an is empty call on that if this is the case where we're going to have the notice say there are no upgrades at this time because really what this means is the store is connected we could get products but there just are no products being returned the error one is slightly harder to simulate although it definitely is possible this one is quite easy to simulate though all you need to do is have products up here that you do not have configured on the App Store so if we had a one after all of these and rerun this you'll see there are no upgrades at this time is displayed because none of these products here are available however as long as one of them is correct you'll be able to see that there is a connection to the store so that error is not actually hit one other quick thing I could show you is if you want to see which of these product IDs in this list are not actually going to be displayed you can also print out the products that aren't returned so these would be the not found products and this will just be the product details response of not found IDs if you rerun this you'll see those two IDs that are not actually configured come up as not found this is useful for debugging if you're not seeing what you would expect to see you might you might have the IDS incorrect either in the code here or in app store and they will definitely need to match up I'm going to remove these two prints here and I'm going to set back the IDS to the correct ones so now we are retrieving our product information and saving it to this products list in the next video we'll display that out in the view in the last video we set our products list here with list of the products that we're getting from the store and that's based on those product ideas that we're setting at the the very top in this video we're going to display that information on the page so since this is a list of information we can use the list view builder for this but we are going to want to wrap this in an expanded widget and the list view Builder here is going to take the item count and the item count is going to be that products. length and then for the item Builder here will have access to the context as well as the index so we will Define the actual product detail that we're dealing with as a variable here just to make things a little bit easier so this will be a product details and we'll just call it the product details and it's simply going to be the products list at the index that we're currently at now we can return a card from this list view Builder and the child of it will be a row which for now we're just going to for the children add a text item and the text it item is going to have the product details title so we're going to use this product details here and then we're going to call Title on it and we can just go ahead and add some of the themes style to it for headline five this is going to need a semicolon and now we should be able to see the different products that we have so I actually do have a typo here which we can fix which will be product details and update both of those if we run this again and go back into the store you'll see that we do get those three product titles that we did configure after this text here we can go ahead and add the description as well and this will just be the product details. description we will need a Comm there and I'm going to change this row to a column so we have the title and the description and these are the values that we configured in app store connect so you can see this is a lowercase decisions here and the rest are uppercase so if I go back into App Store connect and we click into this we'll be able to see in the locals here that this is lowercase and we can make that uppercase and this is just illustrating that this is where the information is coming from and you can update it all right so we can actually go ahead up here and uncomment these other three CU we're going to style the cards now and the cards will have a slight difference to them depending on the type so we're going to make these first three cards kind of look the same and have the same icon over here and then we're going to have these all have different icons and the text of the button will also be slightly different because subscription buttons are not really a buy now it's more of a pay for the subscription so anyway let's go ahead and build out these cards more so first all we want an icon over here but it's not always going to be the same so we're going to create this as a widget of its own and we're going going to call this get Ina purchase icon and we'll be passing it the product ID so if the product ID is the premium then we'll return an icon that is the icon brightness 7 outlined and it will be a size of 50 I'll actually just go ahead and fill in this code here because it's pretty simple and then I'll quickly explain it so basically premium is going to be the brightness 7 icon which you can kind of see over here the unlimited monthly is going to be this icon and yearly will be this other icon if it's any of these three decision ones it's just going to be this add number icon this is kind of just simple styling but it is more of a way to see how you could kind of style these differently so up here we want that icon to display on the left so we're going to wrap this column in a row and then within the row here we can call that icon and we will need to give this the product details ID because that will be the product ID and if we save that you can see all those different icons are there now and we are just going to wrap this in a bit of padding and that looks better now we can also wrap this column here in a expanded widget because we do want that to take up as much space as possible and then in this column here we're going to set the cross axis alignment to be the cross axis alignment of start and that is just going to move all that text to the left there uh last thing we're going to do is add a button on the right so this will be an elevated button we can reverse these and the child here we're going to actually create another function for on pressed for now is just going to be uh nothing and we'll come and fill that in later for the child we're going to create another widget which is going to be the buy text and we will pass that the product details we now need to Define this and we can do that right here below the icon with this we want to put the price on the button itself and then depending if it's a subscription we need to say if it's per month or per year so if the product details ID is equal to the unlimited monthly then we're going to return a text widget which will have the price and the price can be found in the product details it's actually the price parameter and then we want to say that it's that price per month so we'll add this slash Monon after it very similarly we can do the same thing for our our yearly one the difference here is we're going to be using year and lastly we'll return text that says buy for and then the price so buy for $1.99 for example we can remve that part at the end so this will be the the text that goes inside the button and we already called that up here so if we save this you'll see we get that that button which does have the the price buy for 99 and you can see they're all different and then the subscriptions have the month and the year the only last thing that I want to add is a bit of padding around to this elevated button and with that this looks pretty good our store is basically looking how we want it to the main next thing we're going to have to do is actually make these buttons work all right so if you've been following along and doing this kind of quickly you might notice that your store doesn't have any items in it even if you set this up correctly with the correct product IDs and you can see mine is showing everything now all six of my products but if I were to try and add another product here a new product it's not going to immediately be available to the app and if you are developing that could be a bit of a pain because you want to be able to test this stuff and develop it right while you're doing that so for iOS we can actually test our products using storit and what's stor kit will allow us to do is just create basically test versions of all of our products and then connect to the storit basically local environment of of these product versions and then use that instead of the actual live production ones so I'll show you how to set that up right now we'll first need to open up our app in xcode and once that opens up we're going to go to file new and then a new file and we're interested here in creating a stor kit file so if you search if you search for storit configuration file you can find that and we'll hit next and it's going to ask you what you want to name it what you want to call it I'll call it the course configuration store kit and just create it once this is created you can see we have the option to add in a purchases and subscriptions so down here we can add all those different types uh starting with our consumable in our purchase and you can see what we need to fill out here is essentially the same thing that we filled out in our app store connect over here so we are going to want to make sure that these product IDs match up exactly and that is going to be what this product ID is right here the rest of the stuff we are going to want to match up as well so 99 Cents there reference name is going to be here and then it's the same thing with the localizations it's going to ask for that display name and description so really all this is going to let us do is uh have this local version of all of our all of our inapp purchases and then use this information and it'll be available quicker and you won't need to worry about having it set up on the App Store connect to actually use this storeit configuration for our app we can go to the builds Tab and then choose schema and we'll click edit schema and this is going to allow us we want to make sure we're on the run in the options here and you'll see this option here for store kit configuration you can see right now that is set to none so we are going to change that to our course configuration store kit and then we can close out of that and now if we were to run our app from xcode itself we should see that the app actually only loads that one individual in app purchase uh however if we do go back into our Android Studio here and rerun the app we're still going to see all of our Ina purchases here all right so the app is now loaded up running from xcode and if we click on the store here you can see that the store only has that decision from the actual store kit configuration so now that we ran the app from xcode and this is the iPhone 12 with the store kit configuration when we run it from flutter which currently it is running from flutter right now in this tab right here if we click on the store now you'll see it is actually using that store kick configuration so do make sure you first run the actual device from xcode and then run it again in flutter and it should work I just restarted it from Android Studio here so you can see if we go into there we do get that store kit configuration there however if we stop all these and if we still go back to our iPhone 13 and run that this one was never actually run from xcode so you will notice that all of these are still going to exist here if you want to use this iPhone 13 with store kit all you would have to do is run it from xcode first and then everything should be working after that overall I think store kit is a good tool to know about and is potentially useful when initially setting things up because you can get that quick demo of some inapp purchases but long term and while I'll be developing I don't really think it is that useful so I did want to just show you that in this video but for the remainder of these videos I will not be using the store kit configuration I'll actually just be using the configuration that we already set up in app store connect So currently our store view has all of our Ina purchases but if you try and buy one nothing happens so in this video we're going to set up the action to actually allow this purchase to go through and Grant you the five decisions so you can see here in our store view if we scroll down we can find where this button is being clicked and the onpress right now is just blank so we're going to go ahead and add a little bit of logic within here so because we have two different types of Ina purchases those being the consumable and non-consumable we do need to have this button act a little bit differently depending on the type so first we can handle the type of non-consumable and currently our only non-consumable is going to be this premium adree experience that is the only non-consumable item we have the subscriptions will actually get grouped in with consumables but they are also kind of their own category here we need to decide if this product details is a consumable or non-consumable so we want to see if it is a non-consumable and currently we can just use the ID since we only have one ID and it's going to be that premium YT for a non-consumable we can kind of just hardcode this when we have a non-consumable we're going to use that inapp purchase inst and then we can call Buy non-consumable on it and this will take the purchase perams which we have not yet defined so we're going to define those right up here underneath the product details and this will be another final and it will be a purchase param type and the purchase per Ram is defined by the product detail so we can use the product detail from above to create this purchase param and now this purchase Pam down here will be working and that should work for our non-consumable and if it's not a non-consumable it means it's a consumable and we can use similarly the inapp purchase instance and instead of calling by non-consumable we will call by consumable and the purchase prams will also just be that purchase prams the reason this is going to work is because this whole list view Builder all this code is within the item Builder so purchase pams and purchase details is going to be redefined for each of the individual cards within here uh but anyway this should be okay so if we rerun the app now and we go in here and we click on the five decisions you're going to see it's going to ask to sign in with an Apple ID so we are going to want to create a test Apple ID that we can use and test out these purchases without actually being charged if you were to use your actual Apple ID you run into the risk of actually being charged for these test uh inapp purchases so over in app store connect we're going to click on the users and access tab up here and then we're going to go down into the sandbox Tab and click on testers and you can see right here I already do have two testers set up but I'm going to add a new one here and you're going to just give it a name and an email it is important that this email actually works the password Here you do need to make a strong password so I'm just going to go ahead and use the suggested one there and I'll copy that over into my notes then the secret question I typically just put the password in for these because these are just test users that can get thrown away uh enter anything for these and then click invite so you can see I already have a Apple ID associated with this and one nice thing if you have a Gmail you can use the plus and then put any number after it and you'll still receive the message but it is but it does make it a unique email address address so then I should get an invite for that and this is the invite I get and I can now verify the email address and we'll just have to enter that password which is going to be saved right here and then hit continue and now we have this course user here that we can use to test out everything so we'll close out the email here we can use that Apple ID D now which is that and the password here I will paste in and hit okay so again we were in the middle of buying this five decisions so if we look into the logs now we're going to see that we are kind of getting an exception here that's logged and this is to be expected at this point because we didn't really set up any handling of these purchases so you can see it says there's a penny transaction for the same product identifiers please either wait for it to be finished or finish it manually by using complete purchase so the purchase here was never actually completed and we will need to set up some logic to to actually listen for purchases from the app store and then Mark them complete in this video we'll set up the listener to listen to the buy events for our Ina purchases so we're going to do this in a new service class so create a new file and it will be a dart file and we're going to call this the in a purchase service or IAP service I guess will be quicker so this will just be a class so in this class we can create a new void function called listen to purchase updated and this is going to take a list of purchase details so it will take a type of list and the item and list are going to be purchase details and we'll just call this the purchase details list uh that should be lowercase though so within here we're going to have this list of purchase details that are going to be passed essentially from our store view button here so when either of these buy buttons are clicked the inter up purchase package actually has a way that we can alert ourselves with a stream of these purchase details that are getting bought so that is what we're going to be using here really the purchase details list here is going to be what's sent from the actual buy button but we need to call this outside of here and we'll call this in that stream that actually will listen to it this function here is actually just going to be marking the purchase complete once we rece receive that notice it'll be a bit easier to explain as you see it being built out uh we will need to include the package though for the inap purchases here and really the simplest form of this function here is going to be just to Mark the purchase as complete so we are going to have this list of these purchase details so we can just Loop through this and for each element which is going to be a purchase details because that is the type in our list here we can also just name the element within this purchase details and now we can just check if the purchase details so the basically the details of our purchase here are pending complete purchase and if that is the case we're going to go ahead and complete the purchase so this is going to be an await call which means we need to make this an async function and we're going to a wait here and inapp purchase instance and then we're going to call complete purchase and we will pass in the purchase details so I actually placed the async in the wrong spot here the function itself for listening to purchase updates isn't necessarily asynchronous but each of these items that We're looping over will have an asynchronous call in it so we're going to place the a Sync here and you can see now the await is no longer airing out so this is good if we were to save this you might remember from the last video we if we try to buy this we get that error that there's a pending purchase that has been completed so essentially we need to get that purchase to hit this block of code here so that it can be marked as completed so back out in Maine is where we're going to actually call this listen to purchase updates so open up the main. dart file here and what we're going to want to do out here is listen for the Ina purchase purchase stream and as I kind of started saying before this is just a part of the inapp purchase package and it's a stream that we can listen to for when any of those purchase events do happen so just above the build here we can start by creating a stream subscription and this is going to allow us to listen to that purchase stream uh we'll Define this with a late so it's lazy loaded and really all that means is the other parts of the app can load first and this will kind of load afterwards so we're going to be creating a stream subscription here and it will be a subscription of type list and the items within that list are going to be these purchase details the stream will just be a list of purchase details and we'll just Define this as the subscription uh really it can be called anything maybe IAP subscription makes a bit more sense since potentially you might have multiple subscriptions we will need to include the NAA purchase package in main doar as well and now we can override the anit state and within this we can actually now listen to that stream coming from the Ina purchase package so we're going to create a another variable and this is going to be the stream this we can name the purchase updated and this is where we'll use the NAA purchase instance and we'll call that purchase stream on it so we'll have this purchase updated and now we can make use of our inapp purchase subscription and we're going to set that equal to the purchase updated so it's going to be equal to this in a purchase stream and we can call listen on this and the event that is going to be here is going to be a purchase details list and now there's a few things we can do in here so the first thing we want to do is actually call our listen to purchase update since we're calling listen on this whenever anything is updated meaning whenever the inapp purchase package pushes something onto this purchase stream we're now listening to that so this is basically going to get triggered whenever someone actually buy something so because of that we can make use of our listen to purchase update and call that immediately within this block we're also going to just print out a short message here just so we can see it in the logs and now we can call our IAP service and call listen to purchase update on it and remember this does take the list of purchase details which we have now as right here a purchase details list all right so you can see there's several errors here and there are a couple things we're going to need to update you'll notice our decider app class here is actually a stateless widget we're going to want to convert this to a state full widget Android Studio makes that pretty easy once that is done now we can be overriding a nit State we can't override a nit state in a stateless widget because stateless widgets obviously have no state so that fixes that issue now we also have a few issues that are going to come up down here we need to typ cast this Ina purchase subscription as a stream subscription list which is basically the type up here so if you add the as down here it will get us what we want and then we can add that semicolon so now we can when we're setting our inapp purchase subscription here we just need to make sure that it does match the type that we're defining it to be and the last thing here is that we're trying to call this new function on basically this class but it's not an instance of this class so if we add the parentheses it will create an instance of the class and then it will call the function on that so this all looks okay but there are actually two other things we want to handle and the first one is once this is done so once this is complete the listening we want to actually close this stream here and we also are going to want to close this stream if there is an error the way we can do this is actually move this stuff down here and we can call on done done and then simply close this stream so we can close the stream by calling cancel then in a very similar fashion we can also check the error so on error and if there is an error we would actually get access to the the error object here that's returned so you can decide how you want to handle that I'm also just going to cancel the subscription stream in this case and now we can move that all back up there and we shouldn't be using snake case for this so we can update this real quick to be the inapp purchase subscription like that so just to make sure that this is clear the listen here is kind of the main key that is going to make all this work basically what it's going to be doing is be listening for the Ina purchase package to send something to this purchase stream and the Ina purchase package is going to send purchase details to this purchase stream anytime someone purchases any of our Ina purchases and basically what we're going to do then is once one of those new purchase details get pushed onto this stream we're going to pass off that list of details over to our purchase listener which is in here listen to purchase updated within here is where we'll give the actual decisions and then we'll mark the purchase complete there's going to be multiple more steps within this block here and more logic built out but everything in here is actually set up pretty good so let's rerun the app and see if that error is now gone when we try and purchase something to actually illustrate this better we have this print happening here when the purchase stream starts let's go ahead and add another print down here once the purchase has been marked complete so now if we go back into here and we try and buy this you can see we are still getting this error I think what's Happening Here is the error is triggering before sending anything to the purchase stream so let's go ahead and just try and buy a separate one that is different from the five decisions which is pending a purchase right now and what this should do is actually send both of these to the purchase stream but it's not going to get triggered until this new one is made so let me just offense Kate again for this one and you can see down here we did get the purchase stream started twice and then completed and start twice more and complete it again all right so now if we rerun the app you'll see if we go into our store View and click the buy five decisions our purchase stream will start here and and really what this is going to be starting is kind of this whole interaction right now so this is the beginning of it if we hit cancel here or fill in the information and hit okay that's going to kind of be the end of the purchase stream and it will get past into the actual listen to purchase updated so you can you'll notice here even if you hit cancel it is going to get into that Mark complete purchase Mark complete and that's okay that's what we expected there's kind of like no logic around this so everything is going to just be marched complete but there is a difference between marking the purchase complete and actually giving the user the five decisions so you'll notice that was marked complete but if we go back to the home we still just have our three decisions from before so that's something we'll set up later but if we do click buy and were to put in our information it would be kind of the same thing we we aren't actually handling anything but we are listening to these events now in the next video we'll actually build out this listen to purchase update function a little bit more and start giving people what they're buying so now that we're listening to our successful purchases in our listen to purchase updated method here in our naap purchase service we actually want to handle the purchase and give the user whatever that is that they actually purchased so if someone purchases five decisions we actually want to increase their Bank by five and likewise all the other purchases handle those So within here we're already checking if a purchase is pinning the complete purchase but there's other statuses that we can use to determine if a purchase was actually successful and bought we're going to have another if statement up here and we're going to be checking if the purchase details status is equal to the purchase status of purchased and this means that the user actually did just purchase it and the purchase was successful but there is another status that we want to check and that is the restored and we can just add this now as well this will be used if a user actually restores their purchase meaning they bought the Ina purchase on a different device and now they're going to restore it on this device we're going to be setting up the full restoring of purchases later but we can add this part right now and if the purchase was successful here we're going to want to handle that purchase and to do that we're going to be creating another function right within here and we can call this handle successful purchase and this is going to take that purchase details so we need to create this function now and we can do that right below this void function we'll create another void function and it will be called that handle successful purchases and we can also pass in a purchase details type for the one parameter that we're going to be passing into this so depending on the type of app and the Ina purchases you're offering in your app this section could grow in size but this app that we're building here really has two different type of Napp purchase outcomes that can happen the first would be these three which is just an increase in decisions then the unlimited monthly and yearly are essentially the same thing just different durations and premium and the unlimited are both kind of the same category of an inapp purchase in that it's going to be giving the user a different kind of permission for the app so these type of permissions also can just be stored on this users object here so we can just mark it premium true or unlimited true but within here we are going to want to determine which one of the purchases was made and we can do that using the purchase details product ID so we're going to set up a few if statements here and I think it's fine to do this with uh if statement for each of the product types by their ID but if you have an app that has hundreds of inapp purchases which I think is pretty rare you might want to structure this a little bit differently but I think just seeing this concept here you'll basically get the gist of it and be able to modify this if need be so we are going to want to check if the product ID is equal to one of the product IDs we offer we do have those listed out in the store view so we go up to the top of the store view these are our product IDs here so I'm just going to place them here in a comment and basically if it's the first one we will grant five decisions let me go ahead and quickly fill out all of these if statements and then I could quickly go over them all right so as you can see now we are handling all of our different product IDs if it's the decision five we're going to do something within each of these if blocks here and you'll notice the unlimited monthly and yearly is going to share the same block because it will act the same within the app either one of these is going to give you exactly the same access so we can just remove this comment now and because these three are exactly the same in concept we want to create a function that we can just call and pass in the number so we're going to create this in a new service class so we can create a new dart file and we'll call this the Firebase service and we'll need to include the cloud fir store package here and we can just create a class called the Firebase service and now within here we can create a function called increase decision and within this we want to update this user user's Bank value to increase it by a p in value so if we pass in five to this function that we're creating we want this to turn into A8 because 5 + 3 is 8 so this is going to require two parameters so we're going to be taking the uid which is that user's ID and the quantity and we will make these named parameters which you can do with this with these brackets here you don't need to do name parameters if you don't want to but name parameters are kind of nice when you're calling the function later because you can see what parameters you need to fill in so now we can create a fir store instance and then we're going to find the collection of users and then find that users document and now we're going to call the update on this and we're going to be updating the user's Bank value we don't necessarily know what their current bank value is here and we will know this quantity that we want to increase it by but we can't just set this to the quantity because in this example if we had five for the quantity and we just passed in quantity there it would set the bank to five but we really want it to be eight so what we can do here is increment the field value by calling field value do increment and then within here we can pass the quantity so this is basically just going to increment it by that past in value and we're also going to update that next free question this test user does not actually have a next free question at so I believe that's just because I've never actually gone through and asked any questions on this user and if I do that it should get the next free question essentially as soon as you ask a question you'll get the next free question so you can see now that that value is set so so we are going to set that value to the current time and we can use date time. now for that uh within here the syntax is a bit off we do need to wrap this in Brackets because we will be passing this as a map which is just key values and then we can pull that up there and add a semicolon so now if we go back to our inapp purchase service within our handle success purchase we can call an instance of our new Firebase service and then we can call increase decisions and this will take the two parameters one being the uid and the uid in this context is actually currently unknown so we will have to add that and let's add that second parameter which is going to be the quantity and that quantity will be for this one five the uid we will need to know so let's create a new variable called uid and let's initialize our inapp purchase service with that uid so this will be a string value and we'll just call it the uid and then when we initialize the Ina purchase service we're going to require a u ID so now we need to find where we are calling this so if we do a search in all of our code you'll see there's only one location that we are calling this in so we can go ahead and open that up and we do need to pass the uid here but in this context we do have access to the ID and we can get that from our provider through the off service so we'll use the context here and then we're going to read the value for the off service and then we can call the current user and get its uid so at this point if someone were to make a purchase of the decision 5yt the increment decision will be called and will be pass passing in that five decisions here and that increment decision is simply going to increment their Bank value by the amount of decisions PED in which would be five and then also increment that next free question app all right so before we actually test this in at purchase there are two things we're going to do firstly we're going to go into our simulator here and up at the top we're going to choose device and then erase all content and settings this is useful sometimes when testing these because when setting this up we do sometimes get a a actual purchase that was not handled so this is going to clear out everything it's actually going to completely clear out our user as well so we will have a new user ID and this is useful sometimes during testing obviously once things actually get set up and working you're not going to need to do this as much but we're going to go ahead and clear that and then we're also going to add a bit of logging here so we can do this just with print statements but we're going to print out the purchase details status here so that we can see what the actual status is and this will be useful so that we can see whether or not this block is actually getting hit so we could rerun the app we're also going to add another print statement down here just to see if we are handling the purchase these are kind of temporary just so we can see what what is actually getting called when we are running this we'll also print increasing decisions for this quantity all right so we'll rebuild this all right so if you notice we do have a new account ID down here and we can go ahead and try and purchase the five decisions now so this is the only one that's currently set up we will fill in our sandbox test user ID and you'll see down here in the logs we originally had the purchase status of pending that was when the popup first came up to log in then we got the purchase status of error so we did not actually get a successful purchase and you'll notice also that our handle successful purchase was never actually hit and since this wasn't hit it was never passed into the increased decision so we would expect this to still be at three decisions so we can see what this error is now if we go in here and right now we're handling the status of purchased or the status of restored there's also the status of pending if we want to do anything with that but really that isn't as useful but here we can actually check when this purchase detail status is equal to the purchase status of error and when that's the case we can actually handle the error in various different ways but we're just going to print out the error and see what the error actually says so the error is going to be under the purchase details error and this is going to be an optional so we will have to add that exclamation point there now if we run this again we can reattempt our Ana purchase and this time we do also expect it to fail but at least we will see what the error is so we can see here the error is logged and really what the err is saying is that it could not verify the credential that was that was called and basically the authentication failed so our sandbox user authentication failed with the App Store and basically that means that even though this is test information and test data Apple cannot verify that this user can buy anything so I have looked into this quite extensively and spent actually quite a few hours trying to figure out how to get this to work and unfortunately it doesn't seem like you can use these simulator devices on your computer to actually make these test purchases what does work though is if you load this on your physical device and then sign in as a Sandbox test user and then buy the in I purchase from there so let me show you exactly how to do that now so over on the physical device you're going to want to go to your settings and then you're going to want to scroll down to the App Store and click into that and then within the App Store you can scr scroll down towards the bottom and you'll see this sandbox account where you can sign in and here you're going to want to sign in as that same sandbox user that we've created and used if you're on a Mac you can copy and paste from the Mac itself which is kind of nice and I'll add that password and at this point we can click sign in when you're on your actual device it's going to ask you to confirm your Apple ID here and again this is a Sandbox Apple ID so we'll hit continue then it's going to ask me to verify this with my phone number and I should get a text message so I'll hit continue there it's going to send me the code and automatically fill it in and then it will sign me in so now I'm signed in as my sandbox test user so back over here in Android Studio I do have my device connected currently so I'll select that and then I'll run this code on my physical device all right and now our app is running on our physical device you can see currently this test account has eight free decisions left and that's fine so we're going to go in here and we're going to try now to purchase the five decisions so we'll click that buy button there and you'll see here now that this pops up much like it would pop up if you were actually going to be purchasing the inapp purchase but you'll notice it is using our sandbox account so here we can click purchase and we are going to have to enter that password one more time so we can hit sign in there and you'll see that the purchase does go through successfully and this is what we would expect is this popup model here that says your purchase was successful this is kind of a built-in feature of the package here so we're going to click okay and by clicking okay here you'll notice that that is what actually completes the purchase so if we go back we can see we now have 13 decisions which is what we would expect cuz we had eight initially and we added five and you can see in our logs here we're getting all of our logged out information that we added in in those various places so this is all good for iOS the last thing we need to do is going into our handle purchase and finish filling all that out so the 50 and 500 are going to be easy we just can use the exact same thing but increase the quantities here to 50 and 500 we can remove this print statement here and for our premium and monthly this one is going to be a little bit different so we're going to actually just go ahead and cover that in the next video so for our premium and our unlimited subscription we're actually going to be adding a new field to our user object so the onice user object that we're using right now is actually going to be this user down here that has that 13 decisions so when they go premium we want that to just be a true value and then when they're unlimited we want that to be also a true value and these are going to be kind of simple but they will be separate so potentially use could be premium and unlimited this would happen if they bought the premium one first maybe then they try out unlimited for a month either way we want these to be labeled as separate fields on here so we will make them separate Fields kind of similarly to what we did up here we are going to create a new function in our Firebase service and this one is going to be to set the account type and we're either going to be setting that account type to again that premium or that unlimited account type but we will take kind of similar parameters we're going to need that user's ID and we're also going to need the type so either that would either be premium or unlimited and now within here we're going to kind of do a similar thing we're going to have a Firebase instance and then we're going to find that collection of the users and get that document and actually updated so we can kind of just copy all of this stuff here and close that out so now in this one we're keep value here is going to be the type so if you imagine we pass in premium we want premium to be the key and then we just want that to be marked true so we can do that by using the type itself as the key and then for the value it's going to be true so basically whenever set account type is called it will be setting whatever type is passed as true then we're also just going to update the next free question this is good to continually update because if someone is changing their account type we will want to move that next fre question question to now just so that they aren't seeing that countdown window if that is what they currently were seeing this setup should work so now we just need to call it so back in our inapp purchase service we're going to be calling our Firebase service and we'll be calling set account type the uid will just pass in the type here this you can decide yourself how you want to specifically set this up I'm only going to call the type within the user premium and and not actually use the name of the inapp purchase but just call it premium there are several ways you can do this by creating the user types within the user model and then kind of pulling one of those uh but for now I'm just going to leave these as the strings hardcoded here and it will be premium and it will be down here unlimited so this is good we can rerun this on our device all right so now we're on our physical device device here if we go to our app store we can try buying the premium and we'll go ahead and purchase that we will enter that password again and if this worked correctly we would expect only to see an update in our database right now so if we go back here you can see premium is marked as true then we can also just go ahead and test out the unlimited and similarly we would expect unlim to be marked as true as well if one of these unlimited ones are purchased so we can sign in there once we click this okay that's what should actually trigger it and you can see it did trigger that unlimited as being true so all these purchases are working the premium and unlimited aren't really doing anything in our app right now so if you go into the app you'll see everything is the same even though I did buy an unlimited subscription so we need to First further handle these within our code and when premium or unlimited are present we need to remove all the ads and then when unlimited is true we need to actually allow the user unlimited decisions and not even deduct decisions when they're asking questions so that setup will actually be able to test on a simulated device so let's go ahead and start using our simulated device for this what we're going to do is find this user in our database the O y1 which is right here and we're just going add the fields for premium and unlimited for right now we're going to have unlimited be false and we're just going to handle all the premium features and then we can go ahead and handle all of the unlimited features the next thing we're going to do is actually update our account model so our account model right now only has uid bank and next free question the reason this one isn't showing the next free question is simply because we have an asked one uh if we did ask one it will update our next free question there as well so you can see that is there now we want to add two new parameters here one being premium and one being unlimited these are both going to be Bulls so we can add them like this and down here where we are getting our account to Json we are going to update this to include those as well so we can actually break this down into a few more lines but premium is going to be the premium and then very similarly unlimited is going to be unlimited and lastly we want to convert this from snapshot to also include those two new ones so we're going to move this down we can add those last two which are going to be premium and that's going to equal the snapshot data it's basically going to be this block here but we replace this with premium and then we can do basically the same thing for our unlimited and just bring this back up here and now everything is formatted cool so now that we have our account updated here we can actually go ahead and start using our accounts information so the account will know if it's premium or unlimited and we can determine based on that what we should show so the first thing we can update is this account type down here which right now is hardcoded as free let's open up our home View and you can find here right within the build where we have our account type free we're going to extract this out into its own widget so we can copy that and we're going to call this widget show plan because it will basically be showing the plan that this user is on whether that's free unlimited or premium and now we can create a new widget called show plan and we're just going to paste in what we just cut out from the build up there but we will need to return this and we can replace that with a semicolon if we save this everything should look the same because we aren't actually changing anything but but now we can actually within here check what kind of user type we have so the first one we're going want to check is the unlimited so we can say if the account which since this is a stateful widget we're going to have to call this through the widget and the account has the account is already accessible in the home view here we've already set this up so that the account is passed in if you look up at the very top you can see our account is passed in from the the main one we call the Home view so we have access to this account and then we can call the new unlimited parameter on it and we can see if this is equal to true if this is equal to true then we're going to return this text here and this will actually be easier if we don't include the padding down here so we can remove this padding and just return the actual text here and back up here where we have this show plan we can just wrap this in the padding again and basically everything will be back to normal so if we are unlimited here we will want this to say unlimited and if we save that it still says free which is expected because our account is actually not unlimited right now if we go ahead and change this real quick we can see that this will now be marked unlimited because of the way we set up our account with the stream Builder this is all going to get passed in immediately and update immediately so this is all very nice nice because when the user does buy that inapp purchase and change their account type all of that will get updated and it will just sync right down into the account type down here let's go ahead and remove unlimited and just for the sake of showing this we could actually delete this because sometimes the that parameter won't even be there or it'll be null and that also doesn't matter because of the way we set our account with this being an optional lastly we can add an else if here and we're going to be checking if that widget account is premium and we'll be doing the same thing checking if it's true if that is the case we're going to return basically the same thing but we'll just be calling it premium and then the else here is kind of optional uh you don't necessarily need it because it will just return the text if it's last thing there but I think it's nicer to add it personally so this is good if we save this we should see we are now premium which is what we would expect because we were marked premium here and now we are a premium user but the premium user shouldn't see any ads if you go back into our inapp purchases here you'll see the premium is an ad free experience in the next video we can actually start using our account type to hide certain things in the app all right so now we're tracking if a user is premium or unlimited but we aren't actually doing much other than changing the account type here if you look in the actual descriptions of our inapp purchases the premium user should have an adree experience but currently there are still ads here you can see there's an ad in our list view here we have ads coming up when we ask a new question the interstitial we also have the option for the reward ad which that should go away as well if you're premium so anyway let's get into determining if a user should see ads or not the first thing we're going to do is go into our account model here here and create a getter that is just going to return true or false if the user should see ads or not so this is going to be a bull and it's just going to be called add free and it should return true if the user is either premium or unlimited because that means they are an adree user so we're going to check if premium is equal to true or if unlimited is equal to true and if that's the case we'll return true and if not then we'll return false depending on your apps logic this could become a little bit more complex but this is all that I'm going to need in my app and I did miss the return here so add that in now we can go back to the home View and up at the top of the home view is where we kind of load all of our stuff for the ads right here in the did change dependencies you can see this block right here is actually where we load the banner ad and we also create the intertial ad and we create the reward ad so if we want to get rid of all the ads then we can wrap this whole thing in an if statement and we're just going to check if the widgets account is add free and if that equals false then we will want to show all this information so we can move all of this into that block and if it's not then we're going to set the state of a couple things here so we want to set the state of our Banner ad and that should equal null because we don't want to have one and then we're also going to set the state of our intertial ad equal to null and lastly we will set the state of our reward ad equal to null if we rerun the app now you can see all of our ads are gone you could add some logic if you'd like to keep maybe the reward ad even if the user is premium because it's not an ad that's necessarily in your face but it is an optional ad I'm going to leave it off even if they are premium but the logic for that would be pretty simple we already have our create reward ad function here so basically you can add another else if and check if the account type is free but the account is premium then you can just show just the reward ad and then if not show nothing but anyway this is going to be good for this part of it there is a little bit more logic that we actually include around these ads that isn't just here in our home view down in our build here we have the build reward prompt and we don't need this if we aren't going to show the reward so we can also add an if statement here that is going to check that account and see if it's ad free if ad free is equal to false here then that means that they will be seeing ads so then we will show so then we will use the build reward prompt there but that actually should handle it mainly because we are setting all of these to null if you look back through the code all of our logic that is around a banner ad for instance you can see is we already are checking if the banner ad is null basically just show this blank siiz box then even in the show interstitial for instance we're checking if it's not equal to n so if it is equal to n we're not going to do anything which in the case of this now it is going to be equal to n so all of that should be okay we can test this again with a question and make sure we don't get that interstitial which we didn't and this all worked pretty good but if we do go to our history view here you'll see we do still have an ad there and that is actually to be expected right now we are going to need to go to the history view itself and update it so this is going to be slightly different if we look down towards the bottom here we have this set state which is injecting an ad into our history view here so we already have this block kind of marked with a comment that this is adding the banner ad here and if you remember this logic here is basically taking our history list and looping through it and adding an add in every so often so all we need to do here is actually check the same thing if the account is add free but but our history view currently does not have the account linked to it so we can fix this by requiring the account being passed into the history view as the account there and we will need to import that package for our account model and similar to how we're requiring it on the home view we're going to copy that over into the history view but just change the history view there so this is going to break now our call to the history view so if we search that we're going to see where we're calling the history view in our code and that is going to be right here in the home view actually so the home view does already have the account so it's going to be simple to just pass that in as the widget. account and now if we save that we actually do get an error here but we we basically need to rerun it because we don't want to be on the history view we need to be on the home view so we can reload the history view with that account so now the now the history view has access to the account so we can add that if statement and we can say if the widget now because this is also a stateful widget if the widget account is add free and we're interested if that is false because if the account is not add free so if it if ad free is false then we're going to be showing ads so we can put all this ad code right in there and we can save that but let's go back and rerun the app and you'll see since we are premium if we go in here you'll see no ads are loaded if we go back here everything is now add free so there's one last situation we need to handle so if the user is on the free type right now and you'll notice there are ads and and that's fine so if we go back and now we mark them as unlimited or premium it doesn't really matter which one but Market as true you'll see some things get updated really just that button went away but the ad is still down here also the interstitial would probably still show up if we ask right now yes it did but this should not happen because the user has just been upgraded to unlimited so that would be a bad experience the reason for this is because we only are doing our check here if the user has the ad free type in the did change dependencies and that is actually not going to be changed unless the app is completely restarted so if we were to restart the app right now then everything would be okay but we want to handle that situation where they are updated so that it does work immediately and it's actually not that difficult to do we just need to also check the account type when we're later trying to show those ads so up here we're not going to load them if the account is add free but we also don't want to show them at all when the account is add free so we did kind of already do this for the reward prompt which is why you might have noticed the button went away but we didn't do this for the banner ad or the interstitial ad so the banner ad here we're checking if Banner is null but we also need to check check if the widget account type is add free so this time we're going to use widget account type add free equals true because if it is an adree account then we want to just show this blank box there so that should take care of the banner the next one is the interstitial so we have the show interstitial function down here and we're just checking if the interstitial is not null but we also need to check here that so if ad free is false and the intertial is not null then we'll show the intertial ad lastly within the reward ad here we're going to do the same thing and just check that the reward ad is present as well as the account type ad free is false uh to test this real quick we're going to Mark unlimited as false update this rerun the app we should see our ads here and that is good we have the intertial add happening and all of this looks correct now we're going to go ahead and Mark our app as unlimited again and just go ahead and update that so at this point you'll see all of the ads are removed and if we try and ask another question the intertial ad does not come up anymore and already this page was fine because it was going to be reloaded but yeah this is definitely a better way to handle upgrading someone's account because you can imagine the change on their account would actually happen from the inapp purchase so if they were to go in here buy the purchase then go back here and still see an ad that would be not a good experience for them currently we are on an unlimited account here but you can see we still have only one decision left and if you read our description of unlimited we will have a unlimited amount of decisions with an adree experience so the ad free experience is working but we do not have unlimited decisions because you can see if we use this last decision here we now see the count down and that is just how all the other users would experience the app so we'll update this now and there are a few things we need to change the first thing we can change actually is the description up here where it says decisions left one we can change that based on the account type and show that as unlimited so the decisions left part will leave the same Within this here we're going to be checking if the account type is unlimited so we can say widget. account. unlimited and if that equals true then we're going to display the text of unlimited and if it's false then we will just display what we were disain before which is the bank amount so if we save this you'll see we now get decisions left unlimited and I'm actually going to capitalize that but that looks good this doesn't actually change anything you can see if we still ask a question and it's it says unlimited but it doesn't act like it's unlimited so to make it act unlimited there are a few things we will need to change the first thing is going to be in this next free countdown so we can go down here and find that widget next free countdown and if you read this logic here we're checking if the app status is waiting and if the app status is waiting then we're going to be showing that countdown and additionally to that we're going to see if the app status is waiting and the account type is not unlimited so we'll check if unlimited is not equal to True which currently it is equal to true so now if we asked the question here you'll see we don't get the countdown anymore however we don't actually have unlimited features and if we if we look at our account right here we actually have zero in the bank so this is simply just hiding that countdown uh but that is necessary the next thing we can update is setting this app status here we're going to mark this ready if the widget account bank is greater than zero or if the widget account unlimited is equal to true so if we are unlimited then the app status will always be in a ready State this right here Will more or less make this line not relevant however this is good to keep here for the same reason that at the end of the last video we also added a check to the account type and really the only time that this is going to be used is if we are in that transition state from an from a free account to an unlimited account there's only one more place we need to update and that is going to be in the answer question so currently in the answer question we are subtracting one from the bank every time but if a user is unlimited we don't want to subtract that amount from the bank so we can do this with a quick if statement here and we're just going to be checking that account type again and if unlimited is not equal to true so if it is not unlimited then we will deduct one from the bank uh if it is unlimited all the other logic will stay the same but we will not decrease the users Bank by one this is important because imagine you have 10 decisions and then you upgrade to unlimited for a month and then your month expires and you decide you don't want to pay for it when your account type goes back to the free version we want you to still have those 10 decisions that you originally had so having that unlimited status will not count towards your past bank if we rerun the app now we can look at our user which is the user right here we can see their bank has one and we can ask a question and you'll see they can just immediately ask another question there's no countdown here they can keep asking questions over and over again and it's just going to work they all show up in the history and if you look in their account here there is no deduction to their Bank as there was before so that is it now the unlimited accounts are working correctly and just to also show that this is going to work if we make unlimit false and click update you'll see now the unlimited went away here we don't quite see an ad yet which is okay but if you ask a question the countdown comes back and they will have to wait to get that new free decision if we were to rerun the app or if they were to exit the app and come back into it the ads will now show which is what we would expect to to close out this section we're going to just run through some testing on an Android device here and really everything we've done so far should already make it so our Android device is set up and working correctly Android will be a little bit different than iOS in that if you go to the store nothing is going to show at all but that is actually to be expected Android does not link your live version of your products into your simulator kind of similar to how iOS doesn't allow you to purchase them on the simulator but on your actual physical device you can load up Android and I will show you exactly how to do that now all right so we're on the physical device now and we have a new user here and we can find that user here starting with XJ oov and we can see their Bank just has three and they have no updates to their account type so first let's go ahead and buy a decision package we can buy this person 50 decisions and Android is going to be a little a little bit different with our testing accounts we don't need to set up an actual test user all we need to do is have our test app working and we did set all of that up earlier so as long as you're using a test build you will have the option to use a test card which always approves within here you can also test a couple other situations where the card does not approve and where it takes a few minutes to approve and you can test all those situations within your app and handle errors as you would like but we're just going to go ahead and use this always approves and buy it so once that payment is successful we would expect this bank to go up to 50 which you can see this increase to 53 right there and our app is looking good as well now we can go ahead and try to upgrade to the premium which should remove the ads and I have already tested that on this device so we are seeing an error there I can go over how to handle that in a second let's try our unlimited year and this one I have not purchased yet so if we click subscribe we should be upgraded to the unlimited subscription and it looks like that's actually not working so if we look at the errors here a little bit you'll see we get an inapp purchase error that says consume purchase failed and the source of Google Play and that we have an invalid skew type so if we go back into our code in our store view where we actually make the purchase here for our inapp purchase you'll see if it isn't a premium one we are going to buy with the consumable and this works fine for iOS but in Android the subscription needs to be handled slightly differently so what we need to change here is how this onpress is handling the inapp purchase for Android we actually need to change the way this purchase pram is being set when an Android device is being used and additionally our logic here is actually not 100% accurate in that a non-consumable on Android is going to be a subscription So currently this is assuming that the subscription is a consumable but on Android the subscription would be a non-consumable and it turns out for iOS the subscription could be either one so anyway let's get into this and update how this purchase pram is set so up here on line 86 we are actually defining the purchase pram and this is the same way we will Define it for iOS but let's go ahead and move that down into the on press here and we'll change it around a little bit we're just going to Define this first as a late and that will just give us the Vari aable this code down here is what we'll use for iOS but first let's create an if statement here and we're going to be checking if the platform type is Android and this is going to require us to add a package if we haven't if we don't already have it and that's going to be that Dart iio package but if we are using Android we're going to Define our purchase pram as a Google Play purchase pram and this is going to take a product details which we already have defined up here as our product details so we can just copy that over and currently this is not included you can see if you try and do the auto include it doesn't really have a great option there but with this Ina purchases package there is a another package that this depends on so we actually have the package already available we just need to add it in and that package is the inapp purchase Android inapp purchase. Dart so we'll want to include that there and then you'll see that this is now available there is one last parameter here which is the change subscription param and this will be used if we are updating a subscription but for right now we're going to put this as null and we'll add the semicolon there so this is good for if we are using Android and now if we're using iOS we're going to pull this information back and just set the purchase pram to that so this is good now we want to update this if statement and earlier when we were setting this up I mentioned we could change the way our product IDs are set up and not just hardcode this one here and actually we are going to do that now so scrolling up to the top we have our list of our product IDs we're going to break this into two lists one for our consumable and one for our non-consumable and then we'll continue to have the product IDs list that just contains both of them so we'll just create two new lists both will be lists of strings and it will be a consumable IDs list and then I'll copy this and this will be the nonc consumable IDS and now we can just pull the consumable IDS into the consumable IDS list and then pull the non-consumable IDS into the non-consumable ID list and lastly our product IDs list is now going to be the combination of these two so there are a few ways to combine two different lists into a third list but the easiest and shortest way to write it is to use the three dot notation and enter your first list and then do the same thing for the second list and basically what this is going to do is include all the items in this list and then after that include all the items in this list and they will both be inside of the product ID's list so this allows us now to use this consumable IDs list to determine if we should be using the consumable buy so down here where we have this if statement we're going to instead check if our product ID is in the consumable list so we'll do consumable IDs and then we can call contains and we'll be able to pass in the product ID so if this is the case we want to actually use the buy consumable so we are going to be changing this up a little bit and in the else we will just move this down here so it will be a non-consumable overall what this is going to do is update the way purchase per Ram is set and if it is Android it will set it correctly and if it's iOS it'll be set the way it was being set before and then if it is one of the consumable IDS will be using the buy consumable and if not this will include the subscriptions now we will be using buy non-consumable so now we can run this on our physical device and reattempt the subscription all right so here we are on our physical device and we can now attempt to buy the unlimited subscription so if we do the unlimited year here you can see we are already subscribed to that so we just hit okay and let's try the unlimited month this one we are not yet subscribed to so we can go ahead and subscribe to that this time the subscriptions should work and give us the unlimited month so our app should now be updated to the unlimited plan which you can see it is there's no ads and we have unlimited decisions now so that all looks good we never actually tried the premium one so this user here which is showing was never upgraded to premium I'm going to change the unlimited to false so that we are not in the unlimited status and you can see the app did immediately respond to that and update so when we try the premium here you'll see we do get this error that we already own this item that because earlier I did buy this and now my account is marked as owning this so when this happens and this is kind of expected Behavior you can't actually Reby a non-consumable product so you will see that error and the way the user would get that consumable product again because you can see right now I don't have premium but if I want to go in here and get premium there's actually no way for me to get premium so we need to add a restore purchases button which will allow the user to restore their past purchases in the last video we saw that we couldn't repurchase the premium inapp purchase and that's because it's a non-consumable purchase and Google is not going to allow us to repurchase non-consumable products in iOS we can clear our purchase history which does make testing a little bit easier and I can show you how to do that real quickly if you go into your sandbox users here and you click edit you can select your sandbox user and hit clear purchase history that is going to allow you to repurchase those non-consumables in Android however it is a little bit different they do let you cancel subscriptions so there's two ways you can cancel a subscription firstly you should receive an email that looks like this when you actually make that subscription purchase and this is from the test one that I was doing in the last video and if you click on manage subscriptions it's going to bring you to this link where you can see the subscriptions you have so you can see my unlimited monthly one is still active and I can go ahead and manage this and click cancel subscriptions and it's asking if you'd rather pause it I'm going to hit no thanks decline to answer and continue and then cancel the subscription so now that subscription is canceled and I should be able to repurchase that inapp purchase whereas at the end of the last video you would have seen if I tried to resubscribe it was just saying I already was subscribed another thing you can look at is in the Google Play console if you go to order management you can see the subscriptions here that were purchased and you can refund the purchases so for instance this unlimited monthly if we click into this we can refund the order here and we do want to click on remove entitlement and then we can click refund now currently it's looking like there is an issue with the Google Play console that is preventing this specific one from being refunded so so it does seem like the Google Play console is actually just kind of having issues right now so typically that does work I have refunded these in the past and it was not an issue even for these subscriptions so that is how you can do that I would recommend if it's not working as you just saw it's not working for me so just wait a little bit and come back and it should be synced up and allow you to refund but now let me show you how to add that restore purchases button in which is going to allow us to get that premium purchase back so we'll start on the store view here and this will obviously work for IOS and Android but I will show you on this Android device and within the store here again this is not going to show any updates we will have a button that will be at the bottom of all of our in app purchases that just says restore purchases this is going to be something that's required by both Apple and Google to get this on either of their stores firstly let's find where our widgets are in the store View and we'll create a new one and it's going to just be called build restore button and this is going to return a row with the children element of a text button the child element here is just going to be text and then it's going to read restore purchases then we can add a little bit of style and all we're going to do is add some text button Style and lastly we can add the onpress and this is simply going to make a call to our inapp purchase instance which we already have created as a variable in the store View and then we're just going to call Restore purchases on it which is part of the Ana purchase package that's relatively simple and we can end this with a semicolon if we save this nothing's going to happen because we actually aren't including this in the build so up at the top below our list here which is our list of elements right below this expanded we can go ahead and add the build restore button and then save and rerun you'll see right at the bottom the restore purchase button is now there and let's go ahead and just Center this by adding to this row a main axis alignment and we'll do main axis alignment of Center and then we can also add the main a access size and set that to main access size of Max so if we save that you'll see the restore purchase button is now down at the bottom unfortunately we can't really test this in our simulator here because we don't have any purchases so we will have to run this on our physical device here so let's run that on our pixel all right so to confirm this is working we are currently on the free account you can see our user ID is the XJ and XJ unlimited is false here also premium is not even included but if we go to the store here try and buy premium again we get that error that just says we already own this item so that's fine but if we now try and restore this purchase you'll see we did get that premium equals true again and now if we go back we are on that premium account type so this is great it's going to allow the user to restore any of their non-consumable purchases so if they have purchased it in the past it will be applied if they want to change devices and redownload it as long as they're logged in with their same Google account one other quick thing to notice since we did cancel that unlimited subscription if we go in and try and do the unlimited monthly you can see we can buy that again as well as the unlimited yearly so before before we were getting that error that we already owned it but since we canel the subscription we now can repurchase it so that's definitely useful to know while testing these out all right so generally we are all set up with our NAA purchases now and we're able to purchase our consumables and non-consumables on IOS and Android there is one last small thing we are going to do in this section and that is actually show a loading animation so you may have noticed when you click into the store here it's just blank for a while and then all of a sudden those pop up when you're actually trying to use the device and you see that and you click into the store you kind of feel like things are just broken so we do want to add a little loading animation there and this can be done pretty simply we're going to add a new variable here in our store View and it will just be a bu value and it's going to be called loading and initially this is going to be set to to true and now we can use this value to actually show a spinner if we are in that loading state which initially when the page is viewed it will be true so we do need to actually set that to false at some point and we'll set that to false once we have products so down here where we actually set the state to there are no upgrades available at this time we do want to also set the state of loading to false because really we're not loading anything anymore we already know that we're not going to get anything and additionally when we do get products and we set them here we are going to set loading equal to false and then finally we can actually show a loading animation in our app there's this expanded widget which is our main expanded widget of the list view but right above that we can do a check and say if loading is available so if loading is true then we're going to create create another expanded widget and this one's child is going to equal a centered widget and then that centered widget's child is just going to have a circle progress indicator and once we have this all set up we can kind of have it all in one line by removing those commas if you save it now you'll see it just kind of spins up there even though we don't really want it to because loading should actually get set to false but the problem here is that this in nit store info is really only going to be called once but our new definition up here is actually called so if we go back and then go back into the store that is when it would have happened but we actually need to rerun the app to make that loading happen because now these are kind of already loaded in here and it's not actually going to take as much time as it would when we first load up the app while that's running there are actually two other areas that we need to update and the first one is actually going to be this restore button So currently that's always going to be showing if we want to hide that when the page is being loaded which I believe we should we're going to need to add a little bit of logic there so if we find the restore purchase button we can simply add an if statement here and we can say if loading is true then we're going to return a container uh if not then basically all of this stuff will be returned this is another situation where you can add the else but in this case it actually seems like it'll be Messier because this is so much longer the last time I did add it because it was kind of a oneline thing but either way that's kind of personal preference and that should cover all of it so now that that reloaded we can click into the store and all the purchases are quickly there so this this is not a good example and we're not able to actually see the loading animation we can actually simulate the animation and this is kind of a nice tool to know about when we're in here in this future and nit store info this is just happening quickly and this is great like obviously we want this to happen quickly but in the case right now we want to actually test this so we can add a basically a sleep to this so we're going to await a future here and it's going to be a future do delayed and then within the delayed we can just put a duration and in that duration we can put any amount of time but I'll just do two seconds so if we save this now and go back and go to the store you can see we get that now 2 second loading and then everything loads up notice the restore purchase button is also hidden when we enter here and then it shows when we would expect it to now this right here there is a connection to the store was kind of placeholder while we were working on this earlier but now that we have that loading animation it's no longer necessary to have it so I'm just going to remove the set state that actually sets that notice and if we go back now and go back in the store looks good so definitely remember to remove these delayed durations because it's pretty much just going to be a pain and slow everything in your app down but they are useful for sure while testing in the next section we're going to write a backend service to actually validate the purchases because currently right now the way we have this set up there are kind of security vulnerabilities here where a user can go ahead and spoof our system and make it think that they bought the inapp purchase when they actually didn't so in the next section we're going to be setting up a way to actually validate these purchases and that they did come from the app store or the Google Play store and that they're actually valid and have been paid for so at this point we have our in purchases set up but we aren't validating that the purchase was successful with the app store or the Google Play Store so what this means is a user could potentially trick our app into thinking that it bought the NAA purchase even though it didn't it's not that likely that this happens but if a user does expose that vulnerability in your app and then share it with a bunch of other users it could be a very big problem this is especially an issue with subscriptions because currently there's no way to expire these subscriptions right now so you can imagine in the current state of the app if a user buys the one month unlimited subscription there's actually nothing set up right now that's going to remove that unlimited feature once the user's one month is up so for example if you were to deploy the app right now and a user bought the one month subscription they might be paying every month but they also might not be paying every month and either way they would always have that unlimited feature this is definitely not something we want especially especially if you're doing subscriptions but this is still a valuable thing to set up even if you aren't going to be doing subscriptions because it is still going to validate that the purchase was completed on the app store or the Play Store and that the money was received by those stores meaning that the money will be transferred over to you so the way this is going to work is we're going to set up a custom backend service where we can validate our inapp purchases and we're going to be writing this with typescript and then using Firebase functions to deploy these so we're still going to be using that Firebase EOS system we will be using typescript for this so we will be creating a whole new backend project that is separate from our flutter app if you have never used typ Ser before don't worry I'm going to show you exactly how to set up everything in this backend service and even if you've never built any type of backend server or any type of backend service all of this is going to be explained and you're going to be able to validate your Ina purchases by the end of this section so let's get started setting up this validator all right so we'll Begin by creating a new project directory and we are going to call this the decider back end so we can change to that directory and now we're going to install the Firebase tools if you haven't done this already so we're going to be doing this with npm so you can check that you have npm which you should I'm on version 8 right now so we can run npm install dasg Firebase tools so while that's loading up we can actually go ahead and set up in our Firebase console here the use of Firebase functions so with this we are going to actually be using Firebase functions and overall we're going to be creating four different ones but for now we need to actually initialize the Firebase functions here so you can see that we need to upgrade our project's billing plan and Firebase functions are not allowed on the spark plan which you can see I'm on right now we need to upgrade that to the blaze plan I believe it's called so yeah Blaze is the pay as you go because functions can actually incur charges but you do have a pretty nice size uh free version on the blaze plan so typically you won't even be charged anything at least not while you're developing uh so that's all you need to do for that but now you can see we're on the blaze PL so we're going to get started with the functions here and you can see the First Command it wants us to run is actually the one we just ran so back over here we can check out how that went and generally it looks like all of this worked all right so the next thing we can do here is actually log into Firebase from our command line so we'll just type Firebase login and I was going to ask you if you want to allow Firebase to collect usage error reporting for information I'm going to go ahead and say no to that but you can choose yes if you like and that's going to open up a window here for you to log in so I will log into my Firebase account and allow and the login was successful there so if we go back here we'll be logged in and now we can actually hit continue here and we'll see the next step that we're going to be adding which is just Firebase and knit now we only really need fir store so we're going to be running Firebase and knit fir store and now there's a bit of a walkthrough that we can go through to set up our project firstly it's asking us if we want to use an existing project create a new project or don't set up a project at all we're actually going to be using an existing project because we already have that Firebase project right here set up so it is referring to this Firebase project in Cloud fire store and you'll see your projects listed here so we're going to be choosing this first one which is decider YT so now our project is going to be linked and now it's asking what file to use for the security rules we can just hit enter here and it will use the defaults which is the firest store. rules and same thing for the indexes we can just hit enter to use the default and now our app is initialized so this got us set up with Firebase fire store but we also are going to need to initialize Firebase functions so similarly it'll be Firebase and knit functions and there will be another walkthrough that we can go through here firstly it's going to ask us the language we want to use either JavaScript or typescript for this tutorial I'm going to be using typescript and now it's going to ask us if we want to use the es lint for this we'll say yes and finally it's going to ask us if we want to install the dependencies we're also going to say yes to this there is actually a warning here that we do need to address so Firebase functions does not actually support node version 17 which is currently what I'm on so if you check your version of node uh you can see mine is 17 the Firebase functions supports up to version 14 currently so this is basically saying that we have the wrong version defined here but we can update that in a second firstly let's open up our project now in our editor so I'm going to be using VSS code for this so we can open that with code dot or you can just open it in any editor you like so once we have that opened up we can go into the functions folder and find the package.json and this is where the engines here is using node 14 so you're going to want to make sure that that is set to 14 which I believe actually if you follow this tutorial it should you potentially will just have some issues if you try and run this locally because you want to make sure you have node version 14 now you won't really have that many issues unless you're trying to run this locally which at some point we probably will do we can use node version manager to actually get a version of node on 14 so all you would have to do if you don't have NVM installed you can look up how to install that but it's pretty simple and you can just say NVM use 14 and you see I don't have that installed so we'll just need to run NVM install 14 and this will get us node version 14 all right and that'll take a bit to run but once it's done we can go ahead and run NVM 's 14 and now if we check our node version you can see we are on 14 so NVM is useful to switch around your node versions as needed this really is only going to come into play when we want to be testing this locally which is going to happen very much later so back in the code here there are a few small configuration things we want to change so firstly if we go to the TS config.js which is our typescript config there's two lines we want to add here firstly we want to resolve Json module to true and we also want to set es module inter op to also be true and then in our linter over here in es lint since we added this linter we're actually going to be prevented from building and running anything that doesn't pass the linting standard before we actually try and deploy it so there are a few rules we want to add here that will just make this a little bit easier as we go and I'll just drop those in because it's not really worth explaining if you want to see what all these do you can look them up or comment down below if you can't find what it is but basically you're going to want to add these three lines but that should be pretty much it for our configuration and next we can start actually building out our app so if you're using vs code as I am this is a good extension to add in which is the es lent and this will just give us some warnings about when we are failing the lunter you don't necessarily need this if you have the linter installed anyway when you try and deploy Firebase functions it'll complain there if there are any errors and you'll have to fix them so this just kind of a nicer way to see it earlier uh but you can see now with that installed we do get these errors here that it can't read the file and really all we need to do is update one line in our es lint RC and if we go under the parser options here we're going to be adding this line which is the TS config route directory and then just set that equal to the directory name this will allow it to find that file you can see now there are no lenter errors there over the next few videos we're going to be setting up the purchase Handler so we're going to begin by creating a new file and this will be in the SRC here notice that we are in the functions folder so we're in the functions SRC and we'll create a new file here and this will be called the products. TS so it's just a typescript file for the products and here we're going to create an interface face for our product data and this is just going to take two parameters one being the product ID and the second being the type the product ID will be a string and the type is going to be either a subscription or a non-subscription now we will be using this product data when we're verifying the purchase how it's going to work is we'll have our verify purchase function that takes a product data type object here so we'll have a object that has a product ID and these product IDs are going to be the IDS of our inapp purchases and then it will either be typed as subscription or non-subscription so this is good for this file we want to set up a new file now for our purchase Handler and the first thing we could do here is actually import that new product data because we will be using that in here we're going to create one new class and this will be the purchase Handler and we want this purchase Handler to work for IOS and Android so we're going to create this as an abstract class and what an abstract class means is we'll have some methods within the class that need to be defined by the subass so really we would never call purchase Handler directly because it's an abstract class we're always going to have a subass of the purchase Handler and in this case we'll have an iOS purchase Handler and Android purchase Handler and those classes will Define the functions in here so let's create the two abstract methods in here and I think it'll start to make a little bit more sense so we'll Define these with abstract and then we're going to have two one for handling a non-subscription and one for handling a subscription so for handling the subscription this is going to take three parameters and it's going to be the user ID so this will be the Firebase user ID and that so that will be a string uh then it will take the product data so it's going to take one of these product datas and we could just call that product data but lowercase and it will be of type product data and lastly it will take a token so this token is going to come from either the app store or the Google Play store and it will be a string type and from here this is going to be returning a promise and and the promise will be a true or false so we can Define that promise here as a Boolean so you can see we are getting a linter eror that the max line is too long so we can break this down into a couple lines so we're going to have our parameters here that our handle subscription is going to take and then this is just defining what we expect that return type to be and this one is just complaining that we have that line there so this looks good so to explain this abstract class a little bit more this handle subscription here is essentially just defining what we expect this handle subscription method to look like so our purchase Handler being an abstract class has this abstract method that's called handle subscription and we expect handle subscription to have these three parameters with these three types and then we expect it to also return a promise as a Boolean but this purchase Handler itself is not going to Define handle subscription as I said we're going to be defining that in the other sub classes now handling the non-subscription is going to be very similar in fact it's going to be exactly the same in terms of the parameters that are given to it and the response type that is given back but we will have different logic within the actual method and we will be writing different code within those sub classes so we do want to keep these to be two separate things so this is good now we have this as shock class set up but we also want to create now a method that can be used by both or any of the subclasses of this abstract class and this will be the verifi purchase Method so we can start with async here and this is just going to be called verify purchase and this is actually going to take these exact same three parameters here and this is also going to return the Boolean type but for this one we actually will Define a method block so in these ones we weren't defining any method block we were kind of just defining them but this one we will actually have a method defined here firstly we're going to be checking our product data's type so if our product data type and we really only have two options of what that product data type can be if you remember it's either going to be subscription or non-subscription so we'll check if it is the subscription and if that's the case we're going to return the call to the subscription Handler which we Define down here so we can call this. handle subscription and then we simply just need to pass in these parameters that we already have so we're going to have the user ID and we're going to have the product data and lastly we'll have that token and we have a couple linter errors here this is expecting an extra indent so we just need to indent that and this is just complaining because we aren't covering every case of this so we'll put an else if here and now we'll check that other purchase type which is going to be the non-subscription and if it is the non-subscription we're going to similarly call the non-subscription Handler so that looks good and if neither of those are the product data type then we are just going to return false so since both of these are expected to return a true or false value we actually can just return that directly here and that will satisfy this because now regardless of which part of the logic this gets hit it is always going to be a true or false value overall what this is going to allow us to do is call our purchase Handler and call verify purchase on it and pass in these parameters here and then regardless if it's a subscription or non-subscription we'll be able to handle that with one of these two classes which will be defined individually for each of the different App Store types in this video we'll extend from our purchase Handler and create the App Store purchase Handler so create a new file here in this SRC and we'll call this the app store. purchase handler. TS so we can import the purchase Handler that we already created and now we can create this class and have it extend from the purchase Handler so since this purchase Handler is that abstract class this app store purchase Handler now is going to be expected to define those two abstract methods which are handle subscription and handle non-subscription so we could kind of copy these over and it'll make this setup a little bit easier instead of these being abstract they can be a sync because they will actually be methods now and the parameters here will be the same you'll notice the product data is not included so we can add that in and we can now make the body for this method here and we'll just have to return a true or false value so right now let's just have this return true we will update this to actually validate the purchases but for now that will be okay we can remove this space up here and we'll get some errors now that none of these parameters are used we can just leave this for now because we will be using them in a second let's do the same thing for our handle non subscription all right so this is good and the shell of this is now set up so what we actually want these methods to do is validate our inapp purchase so we're going to have the token from the App Store so we want to take that token and then decide if the purchase is actually a valid purchase and we can use this mpm package which is the node Apple receipt verifier and this is going to easily allow us to pass in that token value and then get information about that purchase that was actually made so this will kind of handle for us the logic of connecting to the app store getting that purchase information from the token and then we'll have that information that we can decipher and decide was this purchase valid or not so we're going to need to add this package you can see right here the instuctions for we can just copy this and then in our terminal making sure we're in the project that we're currently using we could run that install there and now we should have the package we can go back into our app store purchase Handler here and we're going to add this line which is going to include that package for us as the Apple receipt verifier and now within this class when we create a new instance of it we can have a Constructor that sets up that receipt verifier so we'll create that Constructor here and first we will call Super and then we can call the Apple receipt verifier here and call config on it and then pass in the needed configuration so if you look in the documentation there are a few things that you can add in the configuration we're going to be adding verbose is true and then we're going to need to get the secret and this is going to be the secret from our app store uh we will come back to this one next we'll set up extended as true and then we can set the environment and this takes an array you can either pass sandbox or production right now we will be using sandbox but when you do deploy this you are going to want to make sure that that is production if you want it to work in both you can just have both listed here uh but it does make sense in your production app to only have production and then if you're testing you can have just sandbox lastly we can exclude the old transactions by using exclude old transactions and set that equal to true and we can add a semicolon there so we need to get this secret value here and this we can get from the App Store connect so if we go to the App Store connect and we're in our project here and if we go to the inapp purchases and click on manage you'll see a button down here that's the app specific shared secret so if you click into that you can generate a shared secret so we can copy this now we could just paste the secret right here and that would work all right but a better way to do this is actually to create a another file with these constant variables and just import that into here so let's create a new file and we'll call this the constants do typescript and this will be a very simple file we're just going to be exporting these constants so for this one we have the constant of our app store shared secret and we'll set that equal to the value we just copied over then we can save that and we will need that new line there and then we can save that and in here we can now import that and then we can go ahead and just use it right down here so this is good we now have our Apple receipt verifier configured and the next thing we could do is set up a function that we can call with in here and use that to actually verify the purchase so so we can Define this right down here and this will be a private async function and we can just call it handle validation so this will be the more generic handling of validation and this is going to take just the user ID and the token it will still be returning the promise of a bullion that is good I think this would be fine to just be on one line so I'm going to disable the max line length for that from our linter and what we want to do in here is actually validate this token so we could do that by calling validate on our Apple receipt verifier which is configured right up here so this would be an await call we would call our Apple receipt verifier and call validate and then this will pass in a receipt which is actually just going to be that token now this isn't going to directly return a true or false value to us it's actually going to return us a purchased products type and that will be in the type of a list so we can Define that up here as actually as a variable called products and this will be the products that were purchased in this receipt and then similarly we we will be using our Apple receipt verifier and this has a type on it which is the purchased products and that will be an array and now we can actually use this type and set this equal to the result of our validation here and since this isn't a weit and potentially will have errors we can actually wrap this in a try catch block so we'll start with the try here and we're going to be trying to set products equal to the validation results there but because this is actually making a call to Apple there's potential that there's going to be some errors so we can catch the errors which will be a variable e for errors and we can do a little bit of checking right within here because if there are any errors in getting the products that were purchased then we're not going to be able to handle the validation and this will always just be returning false so there's two main errors that we're going to handle here the first one is going to be if the receipt is empty so this would just mean there's no products returned and that is one of the error types that would get caught here so we can just say if the error is an instance of and we'll be using this apple receipt verifier eror and if it's an instance of the empty error then that means that the receipt is empty uh we can just log that the receipt was valid but it was empty and in this case we can actually return true because it was a valid receipt but it is empty so the validation will come back and say that it is valid but really nothing will happen because there is no products to award this user or validate this user for uh this cas is pretty rare this isn't going to happen that often the other case will be if the store is offline so if we have an instance of the Apple receipt verifier and the service unavailable error is coming through this one similarly we can just log out that the App Store is currently unavailable and if this is the case we do not want to mark this as valid because we were not able to validate it so we will return false here and finally if it's any other type of error we're just going to return false so this covers everything now when we try and verify our receipt and we get an error but we do need a handle when we get the actual products here and we'll cover that in the next video so you will notice there is an error up here with this package and it appears I actually installed this in the wrong directory so we can go back in here and copy this again and this time we want to change to the functions directory and then from in here we want to run the npm install node Apple receipt verifier that looks better but we still have an error here and all we need to do is declare the module for this so if you look it kind of describes that here but we can declare the module right here and it will be the exact same name as this and later we will be actually adding some custom interfaces to this but that is correct for now lastly we can also just run this save Dev for this new module so if we go back to our terminal and just run the install on Save Dev you can see now that we ran that the errors are all gone if you go to the package.json you'll see in our Dev dependencies here we do have that now node Apple receipt verifier which is what we want later we will be updating this to add a few additional interfaces to it but for now this is all good so when we do get purchased products here as our products variable from our receipt so these would be the products that the receipt that we passed in did purchase when we want to validate those we won't necessarily know the type so we'll know the product ID and it will match the ID that we have in app store connect we're going to set up a bit of a helper in our products here and in here we're basically going to create a way that we can convert that purchased product information into a product data object so it'll just be a simple lookup we're going to export a new constant and we'll call this the product data map so it will be mapping to a product data and this is going to just take a product ID and that will be a string again these product IDs are our na purchase IDs then it will be returning product data so pretty simply what we want to be able to do is pass in the product ID and then the return turned the basically the two options up here if we go back into the App Store connect we can see what our product IDs are right here I'll set up the first one and then just fill out the rest so it'll be like this and then we just need to Define what that product ID is so product ID is going to be the same thing again this is going to be what's passed in as this string so it's going to find this block here and then it's going to be returning product ID and then it also needs to return the type so the type here is going to either be subscription or non-subscription and for our decisions yt5 it is a non-subscription so that is what we want there we're going to need to do the same setup for our other six inapp purchases so let me do that real quick all right and now these are all set up so you can see our consumables are all marked as non-subscription as well as our premium and then our subscriptions are marked as subscription so we can now use this product data app back in our app store purchase Handler we will need to include this so we're already including product data so we can just also include the product data map and now within here when we're validating our products there potentially could be multiple so we will need to do this in a loop so we can do a loop through the products that we have in our receipt and we'll just call the item product and we'll just name the item that we're iterating on product and we're going to be getting that from all the products that we have so we can get that product data from the product data map which we did just include up here and actually now that I'm seeing this this is incorrect we want this to be a lowercase product data map and back in here we actually are just exporting this constant so that will also be lowercase we aren't creating an interface here we're just creating this constant of this information so we're going to be using this directly now in the App Store purchase Handler so we can use that product data map we just need to pass in the product ID now so we have this product here and it does have the property of product ID which will be that inapp store purchase ID so now we have access to the product data which will allow us to determine if the purchase was a subscription or a non-subscription let's make sure that the product data is defined so we can do a quick if statement here and we can say if the product data is not defined by using the exclamation point there then we're going to continue and we want to use continue here and not return because we are in a loop so it'll just basically skip over this one and go to the next one if there is a x one but now we can use our product data and determine the type so since we're going to be looking at the type we can use a switch statement here on that product data's type so we'll just be checking out the product data type here and there are two situations of the type again we have the subscription and non-subscription so we need a case now for each of those so we could do the subscription first and then we can create our case for the non-subscription all right so the structure of this is set up once we get through this Loop though we will want to return true and that is because we'll know at this point that we have a valid purchase because if we even get products defined the purchase is going to be valid so this is going to handle if it isn't valid valid essentially and then if it is valid we'll be returning true down here but the logic that's going to happen in here is the most important part because this is where we're going to actually save the information to our fir store database that the purchase was actually made so we are going to want to create those calls to Firebase in another file so we're going to do that in the next video so when we handle these subscriptions we do want to update firestore and we're going to create a new file to handle all these Firebase calls and we're going to actually just call this Firebase calls so this file here will be both making calls to fir store to add our new purchase information so a lot of the data that's returned from purchased products here is going to be updated and saved into Firebase and then it's also going to be updating our user model in certain situations it will be our generic place to make all our Firebase calls we can go ahead and import fir store here this should already be available because of the way we set up our application now we can actually just create this class so we're going to be using export class here and the first thing we're going to do here is create a Constructor and this is going to get us an instance of fir store the first function we want to create in here is want to create or update a purchase so we're going to creating a new purchases collection here and this is where we're just going to save all of our purchases too we can set up that function but we need to actually Define what a purchase is so this will be a sync because it will be a call to Firebase and we're just going to call this create or update purchase and it will take some purchase data and we're going to create a new purchase object type and that's what we will pass into this so this once it's complete is just going to return nothing so since it's asynchronous it has to be a promise and since we aren't going to be returning anything we can just use void and we need to create this purchase data and the purchase data is really just going to be the data that we want in our document so similar to how we have our user data here with all these parameters we need to do the same for our purchase so up at the top here we can create our Pur purchase so the thing with our purchase here is there will be different parameters if it's a non-subscription purchase or a subscription purchase however a good bit of the fields will be the same but there will be a couple that are different so the way we're going to create this purchase type is actually by creating an interface with the base parameters of a purchase and then we're also going to have those additional specific ones for the non-subscription versus the subscription so I think it'll make more sense once you see it if you're not not fully following right now first we're going to export a interface here and this will be the base purchase so this is going to include all the information that both the non-subscription and the subscription share so firstly it's going to have an inapp purchase Source this source is essentially just going to be Google play or the App Store but we can actually create this as its own type we can do this up above the purchase here and we'll have a new type here which is the inapp purchase source and this will equal one of two things it'll be Google play or the App Store this is useful because we can go ahead and use this type later but for right now we'll just place that in there the next thing that both of them are going to share is the order ID and that will just be a string they'll also both have a product ID which is also a string and ID here we don't want those both Capital both of them will also have a user ID and this will be our Firebase user ID so that's a string as well and lastly they're both going to also have a purchase date and this is going to be a timestamp so we can use firestore timestamp for this all right that is good we have our base purchase and now we can have our two different types of purchases which is the non-subscription and the subscription and they can inherit from the Bas purchase and then they can have additional parameters so we'll do our subscription first and we want this to extend the base purchase and then additionally it'll have two more parameters which are going to be the type and since this is a subscription the type is just going to be subscription and then the subscriptions are going to have an expiry date and that will also be a timestamp so this will be the date when the subscription expires and subscriptions will also have a status the subscription status can also be defined as its own type and we'll export that up here and there's three statuses that a subscription can have it's either going to be pending active or expired so we can use that subscription status for our subscription status type we can go ahead and copy this and do a very similar thing for our non-subscription purchase so our non-subscription purchase is also going to extend the base purchase its type here is going to now be non-subscription it isn't going to have an expiry date at all and its status here is going to be another new type that we can create and will be the non-subscription status nons subscriptions also have three statuses but they're slightly different pending will be the same but there is no active non-subscription status instead we'll just call it complete a non-subscription can also be cancelled all right so this is pretty close now so you can see we have two different purchase types it's either the subscription purchase or the non-subscription purchase both of them have all of these parameters and then these parameters within the blocks are specific just to these types so for our purchase now which is going to be the generic type for either of them we can simply use the two and say that our purchase is either going to be a subscription purchase or it is going to be a non-subscription purchase so all of this setup is valuable so that when we call Creator update purchase we can pass in one of two types we can pass in either the subscription purchase or the non-subscription purchase and either one of those objects will work for this so now we can finish building this out so firstly we can Define The Collection that we're going to be saving to and that will be a constant and we can call this purchases which is going to equal the fir store instance and then we're going to call collection on it and then we're going to define the collection of purchases because this is where we want all of our purchase data to be stored and in fir store it's going to once we save the first one it's going to create a new collection right out here called purchases next thing we can Define the document ID so kind of how our users use the their user ID here for the document in the purchases we're going to be using the in a purchase source so we're going to have either Google play or app store and then we're going to have the purchase order ID so this is pretty simple we can just create another constant here so we call this the purchase ID which is going to end up being the document ID we're just going to join those two variables that I just described so we're going to have the purchase data here which is going to just be one of those purchase types and now we can call any of these parameters so we're going to use the Ina purchase source and then we're going to just put an UND score and then we're going to put the order ID so it'll just be the purchase data of the order ID and regardless of the purchase type both of them do have those because they're both in the base purchase now we can actually go ahead and create that document so this will be another variable and this one we can just call the actual purchase and it'll simply be this reference to the collection and then we're going to be creating a new document and this is the name of that document the purchase ID there is going to be our document name now all we have to do is save the data that's passed into here into this document so this is pretty simple but this will be our await call that we're going to be making to Firebase and we're going to take this purchase document reference that we have now and we're going to set the data and we're just simply going to set it to the purchase data here which is already going to be formatted as a key value pair because of the way that our purchases here are already set up as key value pairs if we save this we can now begin to use this call back in our app store purchase Handler so when we have a subscription we can make that call and this will have to be awaited and we'll be calling this with a this.f Firebase calls and Firebase calls will have to be imported here and now we can call this create or update purchases and this is going to now take that object so since this is a subscription we are going to need to fill out all the information in the base as well as what is in the subscription so I'm just going to copy this over and paste it in here because these are the parameters we will need to pass in and it will also require these as well for the subscription so now we can fill out all of these so the inapp purchase source is going to be the App Store because we are in our app store purchase Handler the order ID is going to be this product here and we can call the original transaction ID on it now the original transaction ID is something that is on our product but our node Apple receipt verifier purchased product which is the type of our product here does not actually have this defined in its type which is a bit strange but it's a quick fix up here we can override the purchased product interface by calling interface here and purchased products and then simply adding that original transaction ID as a string here so that should now be okay the next thing is our product ID and this is also on the product here so this one is simpler to call with just product ID and this will already exist next is the user ID which is actually going to be passed in all the way from the handle validation you can see this gets passed in basically from the beginning so we can just basically reuse the user ID value there these do need to be commas next we have the purchase date and this we want as the fir store timestamp so we can keep that and we can call from Mills because our product is actually going to have this in milliseconds so we'll take the product and call the purchase date which is going to be in milliseconds and this will just convert it to that timestamp the type here can be our product data type so if you remember when we set up our product data it's either going to be subscription or non-subscription we do know that this is a subscription so you could just leave it hardcoded like that or you can just use the product data type either one will pretty much work fine and the expiry date is going to be a very similar setup to what we did up here here with the purchase date you'll notice fir store is erroring out and we'll fix that in a second for the expiry date it'll just be the products expiration date and if that doesn't exist we can just use a value of zero this line is now too long but we are going to ignore that next we have the status which for the subscription we want to either set to expired or active so when we want to set it to expired is if the expiration date is less than the current time we can just do the products expiration date and this potentially could not exist so if it's null we'll make it zero and then we're going to be checking if that is less than the current time which we can just use date. now for and if that's true then we're going to set this to expired and if it's not then we'll set this to active all right so that's of our subscription we do have a couple issues here first of Firebase calls is not working and that's simply because we're not calling this correctly so up in our Constructor we want to define the Firebase calls and notice we're using the lowercase here for defining it and we'll Define it as a instance of that Firebase cause so now we want to use this Firebase cuse instead of the capitalized one and you'll see there are several other errors here the problem actually is right up here in our switch statement we're looking at the product prodct data product ID and then checking the case of subscription or non-subscription however product ID is actually going to be our Ina purchase ID not the actual type so this needs to be changed to be the type and now we can check the type subscription and the last issue is the from Mills here and if you read the error there it's actually that I just spelled it wrong it's m i l l i s so if we update that all this should be good so we're just about done we just need to handle the non-subscription which is going to be very similar we can paste all of this in here but we do need a modify quite a bit now if we go back into here you'll see we have a type anastatus which we did already have there so it is the same stuff it just is different values the inapp purchase source is still going to be App Store order ID is going to be the same as well and product ID and user ID purchase date will be the same same too the expiry date is not going to exist so we can remove that line and the only thing that really needs to change is this status here and we're going to change this to complete because if this gets hit it does mean that our non-subscription purchase was successful and complete the last step we need to do is actually call this handle validation but don't worry this is very simple we just need to call it up here in our handle subscription so instead of returning to will return this. handle validation and this takes the two parameters user ID and token so we're going to pass in that user ID and that token then we can actually just copy this line and do the exact same thing down here so now we're set up so that if we call our app store purchase Handler we can call either handle subscription or handle non subscription on it and it will run through this validation here validate our purchase and then update our date database with the actual information from that purchase all right so the last few videos we've been doing a lot of setup and we're now at the point where we can actually Define our verify purchase Firebase function and get it fully set up for the App Store so we're going to want to go to our index file here and this is where you're going to Define all of your actual functions and these are the ones that are going to get deployed to Firebase functions if you remember when we set this up initially we set up that hello world function and you can see that that is here right now by the end of this video we will have a verify purchase function here in its place so we can go ahead and just delete all of this stuff here and we'll start by creating that verify purchase function so we're going to export this as a constant and this will just be a call to the functions so now within here is where we actually can make the calls to verify our purchase so this will be asynchronous and we'll take a few parameters here into this function but we want to create this as its own type so let's go ahead and create an interface up above this and this will just be the verify purchase parameters and there's going to be three parameters that we're going to require when calling verify purchase first one is going to be the source and we already created a type for this Source back in our Firebase calls so this is the source we're really going to want to use here is the inapp purchase Source there so we can copy that over and paste it in here we are now going to need to include that from the Firebase calls and the other two parameters we're going to take are the verification data and that'll be a string and then the product ID and that that will also be a string so now we can use this verify purchase parameters and pass it as the data parameter into this function and we'll also take the context here this verifi purchase we want to return a Boolean so this is actually going to get called from our app and in the app we just want to see if given this purchase data is the purchase valid and if it is return true and then Grant the user that actual purchase so this will be a promise so ultimately we are going to call verify purchase and that verify purchase is going to be from the purchase Handler so we want to call verify purchase but we need our purchase Handler to be the iOS purchase Handler when this is called from IOS and it needs to be the Google purchase Handler which we haven't created yet but when we do create that and a Google app is calling this we need it to to be called from there so we're going to kind of set this up now so that later when we do set up the Google one it will be easy to drop in we want to set up a constant here which lists our purchase handlers so the key of this is going to be using the source here which again is just one of two values Google or Apple so we'll set this to be the source and we'll say it has to be of this type so one of those two values and the value of this will be a actual purchase Handler all right so now we can create the key value pairs for our purchase handlers again we're going to be using those Ina purchase sources so if we go to the Firebase calls we can see what the two values are there so we need basically to create a key value pair for each of these so when it's Google Play we'll use an instance of our Google Play purchase Handler but that we haven't created yet so we're just going to keep that commented out but when it's is the App Store we'll have a new instance here of our app store purchase Handler which we do have created and when we look at this there is the Firebase calls that this requires so we need to pass in the Firebase calls to this Constructor and that will be good now Firebase calls is not defined here so we do need to Define that so right above we'll create a new constant and we'll call this the Firebase calls and this is just going to equal the Firebase calls and with this we're going to need to pass in a value of fire store so we can do that by including the admin from Firebase admin and then we can call an instance of fir store on that admin because we'll be using this admin there are a few other things we can set up so firstly we want to call the admin initialize app and then we want to set up our our functions which we're already using right here we want to set this up to actually Define our Cloud region so we'll set the functions equal to the functions here now we're going to change to be the actual functions object and then we're going to set the region here and this region we are also going to Define in our constants so we'll create a new constant called the cloud region and then in my constants here I will create that cloud region and I'll be setting mine to us Central one and then we will need to import that cloud region from our constants so we're initializing the app we're setting our Cloud region we have our Firebase calls and then we use that in our purchase Handler and let's see there's a few issues here I did forget an equal sign here so definitely need to set that and it looks like we want to just remove that space there and this is going to need to end with this semicolon and lastly we do need to include our app store purchase Handler so that we have access to that and that should be good now it's just basically complaining that this is never used so we can actually go ahead and use it now so down here in our verify purchase function we ultimately want to call our purchase handlers validate purchase so our purchase Handler is currently going be stored in our purchase handlers here and this is going to know to use the app store purchase Handler which is a type of a purchase Handler so here we can just return our purchase Handler which we'll find by getting the purchase Handler of our data source so our data right here is just a verify purchase parameter and the source of it is going to be either that Google play or App Store so we can call this data here and then call call source and now this is a purchase Handler so we can call that verify purchase on it and if we look at our purchase Handler the verifi purchase takes these three parameters which are the user ID product data and the token and this will return the Boolean value which we ultimately want to return from here as well back to our app so let's pass this those three parameters the user ID is going to be on the context the product data we will need to Define so Above This return here we can Define the product data and we can get the product data from our product data map if you remember we have this product data map and all we need to feed it is a product ID so we can use this product data map and we need to pass this now the product ID so we can also get that from the data here because we are setting this product ID on the data so we'll get that and of course we didn't include this file so we do need to go ahead and do that we can clean this up here and add the product data map so now we have this product data and that will be our second parameter because again in our purchase Handler the second one is a product data and the third one is just a token and we'll get that token from our data element again and that will be the verification data all right and I fixed up the indentation there and now we just have one issue with our off here and we are going to handle that above so we do need to do a little bit of validation here on the information that's passed in and make sure that we actually want to make the call to our verified purchase so there's really just three situations that we're going to be checking first one is we're going to check for that user and for this we can just check the context of O exists so really we're interested if it doesn't exist we want to handle that so if there is no authentication we can do a console.log and we're actually going to use the warning here so we can do a console. Warn and we'll say that this verifi purchase was called with no authentication this will be useful while debugging we are going to want to throw an error here so we want our app to be aware of this error so we can create a new https error and this takes two parameters the first one we will pass as unauthenticated and the second one is just the message so it'll be the request was [Music] unauthenticated so you'll see that this here did fix our issue down here with our off now we can check that this product data exists so we'll be handling If the product data does not exist which means that there is an inap purchase that our validator will not be able to handle so similarly we'll do a console. warn here but we can update that message to say that this was called for an unknown product and then actually just print that product ID which will be useful with debugging and we will need to return here as well and we just want to return false because there's nothing left to validate here and we don't want to return true because we don't want to Mark a purchase as valid even though we weren't able to run any type of validation on it the last thing that we want to check is if this purchase Handler data source is unknown so for instance right now we just have the App Store so imagine that someone tries to validate a purchase with Google right now we would want that to actually throw an error because we can't actually validate with Google and then further imagine someone was trying to validate with a third store type we would want that to throw an error as well because we'd only be set up to validate with Apple and Google this is another easy one we just want to be checking if the purchase Handler data source does not exist and if that's the case we are going to do a very similar thing we're going to say that the source is unknown and then we can actually print that source as well and we'll return false so I'll throw a few comments in here all right so this is good now so you can see this is throwing an error that our type Google Play is not available so for right now just so that we can deploy this we're going to actually just set this to the App Store purchase Handler this is definitely going to need to be changed later but it'll be nice to get this live and tested on the App Store first and then we can come back and do the Google play one so that looks good uh we just need to fix this last error there you can't have any linter errors or else the deploy will fail but this is all looking good now so we want to deploy our Cloud function and it will replace this hello world one so in our terminal here under the functions tab we can just run Firebase deploy and you can see here it's going to prompt us that a function used to exist which was the hello world and there's no longer any code for it that we're trying to deploy so it is going to confirm that we want to remove that and this is is pretty nice because imagine that the hello world function was actually a valuable function we wouldn't want to necessarily destroy that accidentally but in this case we do want to remove it so we can go ahead and click yes there all right and our deploy was complete so if we go back to our Firebase console and reload you'll see we do now have our verify purchase function here this is great our verifi purchas is now set up to be verifying purchases from the App Store so in the next video we're going to call this from our flutter app and make sure that everything is working correctly there so now we have our verifi purchase Firebase function setup and we can actually begin to call that from our app itself so back in our project in Android Studio we're first going to include the package for Firebase functions so if we open up the terminal and add flutter Pub add Cloud functions we'll get the cloud functions package here and we'll just be sure to also run pubg and now we can open up our inapp purchase service and right within here we have our listen to purchase and this is what is called when any user purchases a inapp purchase so right down here you can see we look at the status of purchased or restored and then we simply pass it off to the handle successful purchase function and down here is actually where the user will get what they purchased so right before this is called we actually want to verify that the purchase was a valid purchase using our new backend purchase verifier so since this is all part of the same Firebase project we'll already be set up to use the cloud functions that we created so we can actually just make a call to that cloud function we're going to do this in a new function here but we'll get its result so the result from it will be a bullan value and we'll just call this valid so if it's true it means that the purchase was valid and we'll set this equal to a new call that we're going to make to the Firebase function so this will be an await call and we'll call this new function verify purchase and we're going to need to pass it the purchase details so verify purchase here is going to be a function that we create in our in our purchase service so down below the handle successful purchase we can create a new function and this is going to return a true or false value but it will be a future bu and now we can Define this as verify purchase and it's going to take a purchase details which will be of type purchase details and this will be an async function so we can add a Quick Print statement here that says that we're verifying the purchase and now we can get an instance of our Firebase function so we'll call this in instance the verifier and the verifier is going to be a Firebase functions instance and then on that instance we're going to call that Firebase function that we created so we can do that with https callable and then pass in the name of the function so the name of the function is verify purchase and we will need to include that cloud functions package in and it looks like I do have a typo here this is Firebase functions so now with this verifier and again this is just an instance to that function that we created if you remember the function though that we created does take some parameters so if we go back into that code you can see here is our verifi purchase function that we just wrote it takes this data attribute which is actually a verify purchase params type and that type has these three parameters so let's copy these three parameters over and in here now back in Android Studio we're going to get the results of that function so we're going to call the function and and save the results as this variable results and this is where we'll have to await the call so we're going to be calling the verifier and this is where we need to pass in those parameters so we can paste those all in Here and Now we'll need to actually format them so each of these are going to be strings and for the source we can use our purchase details and then get the verification data and the verification data does have a source on it so we'll be able to use that for the verification data itself we're also going to be using the purchase details and we'll also be using the verification data and then for this we'll be using the server verification data and this will need to be a comma as well as this and this one as well and lastly for the product ID that is also going to be on the product details and it's just called the product ID so that's good now we'll have this result which should be a true or false value based on what our verifier function is going to return to us so so let's also just print that out and now these results we can return the results and we really actually just want the results data so the results is going to have a little bit more than just the data but we only want the data and that data will be a true or false value but we do need to typ cast this as a pool so that it matches with everything that we expect here so this is really all we need to do in our app now we're going to be calling this verify purchase and this will pass off all our information to our verify purchase Firebase function which will go through all the logic that we already built out and get us a true or false value depending on whether or not the purchase that we're passing into it is valid or not from our actual App Store but up here we aren't really doing anything with that we're just setting this value to a bu called valid so what we really want to do though is check if this this valid parameter is true so if valid then we will run the handle successful purchase and if not then we won't with this here you could choose to do something else in your app maybe show a modal that says your purchase cannot be validated you weren't charged something like that but for now we're just going to leave it at that so as long as it's valid you will get the purchase handled so we can save this and run it on our physical device all right so here we are on the physical device and there is one thing we want to do before we go ahead and run this function over here in our verify purchase we can actually click on the logs here and have these up and we would expect to see a log entered here when we actually create this purchase now you'll notice currently my user is on the unlimited plan already and we can find our user here which is the W2 user and we can delete the unlimited and delete the premium as well so now it's just a normal user that has 13 decisions now we can go ahead and make the inapp purchase we're actually just going to go ahead and do one for the five decisions and we can go ahead and click purchase here we'll need to enter that password that we have for our sandbox account and this is good so once we click this once the pop-up comes up and we click this okay what we're going to expect to happen here is our listen to purchase update will get hit and then call our verify purchase here which is going to pass off to our Firebase function and really ultimately what we expect is a new table here to be created with purchases and then have that purchase information in there and all this should happen pretty quickly and then that function will return true and the user will be given the five decisions so notice we have 13 decisions now so we should also see that go up to 18 so we'll go ahead and click okay now and you can see we're now up to the 18 decisions we do not see the purchase collection over here though so let's go ahead and check out the logs we added those print statements for verifying the purchase so that part did get called and then we should also expect that call IFI purchase with the following results so everything actually looks correct over here I believe we just might need to refresh the browser here and yes you'll see the purchases is now here and you can see it does have our app store and the ID of our test purchase and then we have all this information here most importantly we have the user ID and then we also have that product ID so that is a good example of the non-subscription purchase type we can also go ahead and make a purchase here for for the unlimited month and this will test that our subscription purchase type is working and you'll see that time it was pretty quick and purchase is here and you can see the type is subscription and notice now that the expiry date is going to be December 11th at 10:08 p.m. the current time is 10:03 p.m. for me so this is what we would expect because our test sandbox data is actually only going to do a 5 minute subscription so all that information is correct this is really important because we are going to be setting something up where we have a function that runs every so often that goes and looks at all these purchases and checks for any that are expired but still active and then it goes and marks them expired and goes and updates that user to mark them as no longer having this unlimited purchase so at this point our verified purchase is working exactly how we want it to for iOS however we do need to Now set it up for Android so we will be creating that new purchase Handler but there is an extra step with Android that we have to configure in the Google Play console so over in the Google Play console we're going to scroll down to the setup down here and find the API access and then you're going to scroll down a little bit and look for the service accounts and then you're going to create a new service account so this kind of gives you the instructions for it you're going to need to do this in the Google Cloud platform so we're going to open that up in a new tab and once you're in here make sure you are on the right project I currently am so you're going to click create service account and now we'll have to fill in this information so for this we'll just call it the decider YT backend you can optionally add a descript description here and then go ahead and hit create and continue then we are going to select a role here we're going to give it the role of editor and then go ahead and continue and then here we can just hit done and now we have our new service account here so we can go back into our Google Play console and you can see it already says the service accounts were refresh so we're going to look for the one that we just created which is the decider YT backend and we're going to go ahead and Grant access now under the financial data we're going to check both of these and everything else should also be checked the only one we'll leave unchecked is this admin and we can click invite user you'll see now it does have our new service account here and a long Google generated service account which is actually has the ID of our app so we can send this invite and now that the account is set up we need to generate some credentials so if we go back into the service account here and click on these three dots here we can find the manage keys and click that and now we can add a key and create a new key and we want this to be a Json key and then go ahead and create that and that is going to download a Json file which we're going to use in a second we can now move that key into our project so back over here within our function SRC we're going to create a new folder and this is going to be called assets and we can drop the key right in there but we do want to rename this key and we'll call it the service account and that looks good if you are using GitHub you can add this to your git ignore because this key shouldn't really be saved in GitHub so you can add that in here by copying the relative path and removing the functions part of it you so you can see that gets grayed out which is what we want so it won't be actually saved in GitHub I'm not going to blur out any of this it'll just be easier to delete it after explaining all this but the last thing we're going to do here is go to our constants and add one more constant for the Android package ID and this is going to be the ID within our app there's a few places you can get this if we go back into the Google Play console and find our app it's actually going to be right here so we can just copy it from there it's also in the code and there's a few other places you could find it but we're going to add that in here now and save that so this is good this service account is actually going to be used when we try and connect to the Google Play Store to validate these purchases so we can now create our purchase Handler and it will be similar how we created our app store purchase Handler so we'll just create a new file here and we'll call it the Google Play purchase Handler and the first thing we can do here is create that class and this will extend the purchase Handler which will have to be imported and if you remember we will need to add those two methods so our purchase Handler again has these two classes for handle subscription and handle non-subscription and and similar to how our app store purchase Handler has them let's just copy them from the App Store purchase Handler and we'll modify so for now let's just return true for both of them and we will later fill all of this out but in this video let's get our purchase Handler Constructor set up so the first thing we need to Define is the Android publisher and this is what's going to be used to actually verify the anap purchases so we can Define that here and this will just be an Android publisher API Android publisher so we are going to need to import a few new packages with npm so if we go into our package.json we can see where our dependencies are here and we are going to need to add Google off library in Google apis and if we save that and then run npm install while in the directory that should add those packages for us and if we go back into here we can now import the Android Publishers API using this line so we want to get it from the Android publisher version three and this is all ultimately coming from the Google apis we're also going to need to to include Google o which we'll be using in a second so we can add that in as well so now that we have this Android publisher we can create our Constructor for the Google Play purchase Handler and very similarly to how we have our app store Constructor we are going to add the Firebase calls in here and this Firebase calls again is going to give us access to updating our fir store database but this will also require us to include the Firebase calls and fire store so we can just actually copy these over and the first thing we'll call in here now is super and then we're going to get the instance of our Android publisher here and we're going to set it to a new Android publisher API Android publisher you can kind of copy that line from there and then we're going to have to pass a few parameters into this so what this is going to require is an off value and for this we will use the go Google off which we just earlier included and the Google off will take a map here of key value pairs the first will actually be all the values from our service account here so we want to pass essentially all of these in and then we're also going to be passing additionally a scope in so for this we need to import that file into here so we can import our file and we're going to just call credentials and it will be from that file in assets and then it's our service account. Json for our Google off we're going to pass it the credentials and then we're also going to define the Scopes and for this the only scope we need is going to be the Android publisher so it is going to be the scope will be this URL which is is googleapis.com sloth androidp publisher we'll add a comma there and another comma here so this is good at this point we have our Google purchase Handler Constructor set up and we're going to be able to use the Android publisher to actually validate our purchases in the next video we'll go ahead and build out the handle subscription and handle non-subscription all right so we have our Google Play purchase Handler set up here but we need to still build out the handle subscription and handle non-subscription methods so right now they're just returning true in the iOS version of this we created a third function and then we're able to kind of use it for both of these that is not going to work as well for the Google one because as you'll see later these will kind of stray much more than the iOS version of this does so because because of that we're actually just going to write the code within them here because it's not going to be as much shared code so the first thing we'll do is actually verify the purchase and we're going to begin with a tri block here so the verifying of the purchase might not work and if it doesn't that's fine we'll just catch the error and return false so we can log a message here for debugging purposes and we'll just say we couldn't verify the purchase and then we'll just return false so in the tri block here what we actually want to be trying to do is first verify the purchase so we're going to be making a call to the Android publisher here and this is going to give us a response so let's save this as a response and the call we'll be making is going to have to be an await call and it will be to the Android publisher and we're going to be calling the purchases and then the subscription because we are in the handle subscriptions block here and then we're going to call get on this and this will take a few parameters so essentially we're going to be getting a subscription purchase and we're going to be passing it the package name and in this case the package name is the name of our app so we already did set this up in our constants and it's going to be the Android package name here so this is basically tying the Android publisher which app we're using this we will have to import and we can do that with this line here for the Android package ID from constants after giving it the package name we also need to give it the subscription ID subscription ID is going to just be the products ID so if we get our product data which again the product data is just going to have the product ID and the type the subscription ID here is going to be the product data this product ID so this will just be the ID of our inapp purchase so we're giving it the app we're giving it the type of inapp purchase and then lastly we need to give it the token so that token will be passed in to the function here and we'll just be able to use it there and end that with the semicolon so we'll get this response of the subscription purchased from the Android publisher but we do want to first check that the response actually has some data so we can check that it has at least an order ID and if it doesn't then we're going to just return false if the response. dat. order ID is not available then we want to return false and for logging purposes we log out that we can't handle the purchase without an order ID and this order ID here we do need to potentially modify if the subscription is being renewed so the way the order ID is going to look and I can show you this from one of the test emails the order ID is going to be the same as this order number here so you can see it has this number and then at the end here there is this dot dot and another number the two there means that this is the second time that this same subscription has been renewed so this has been renewed for a second time but with what we're doing we actually want this only to be the first first value here so if this subscription is renewed we don't want to include this this part at the end here so we can modify the order ID if it does have that ending and just pull out the initial part and we can do this with a regular expression so let me just write this and then I'll explain the code a little bit all right so we first Define our order ID as the order ID from our response and then this line here is basically looking for a pattern here with this regular expression if the dot dot number is at the end of the string to illustrate this a little bit easier if you go to regex101.com and paste that in here and then if you copy one of these order numbers like this and paste it in here you'll see that the Matched part is that first part if you remove the ending there is no match so if it doesn't have the two dots and then a number there is not going to be a match in this regular expression but if it does have that and the number can be anything then it will match on just this first part what that means is if we do find that regular expression then we want to set the order equal to just the first element because when this is called it will create an array with the matches so in this case this would be an array with the the value here if this isn't fully making sense if you just write it out you can also log out what what's going on and see the original and then the new one and really this is only going to be changed if it is one of those dot dot one or dot dot two so I'm going to remove that print line there this is very important to do because of the way we use the order ID when creating our document so if you look back in the Firebase calls we do use the order ID right here for the document name so it is important that that matches especially on the upd of subscription cuz you can imagine someone creates a subscription and then it's about to expire but they renew it we're going to want that same document to be updated with the renewal and make its expiry date later and this will become even more important later when we start to expire these purchases from our database but anyway back in the Google Play purchase Handler the last thing we need to do is actually just create or update that purchase so we were just looking at that in the Firebase call so we just need to call this now if you remember we already did set this up in a way where for the subscription we can pass in a subscription purchase so we can Define that subscription purchase here as the purchase data and this will be a subscription purchase and we haven't included the subscription purchase yet so we can add that in with our Firebase calls and if we go back into our Firebase calls we can actually just copy all these parameters that we're going to need and since it's the subscription purchase we also need these three and now we can start to update so the Ina purchase source for this is Google play the order ID we just defined and these are actually going to be commas not semicolons the product ID is going to be on our product data which is passed in and we can just call product ID on that the user ID here is the user ID this is just passed in originally as well the purchase date is going to be the fir store time stamp from milliseconds and then for this we're going to have an integer that we can parse that is the start time milliseconds and that will look like this then the type is going to be subscription the expiry date is going to be very similar to the purchase date the only difference here is instead of start we're going to use expiry time and lastly the status so our response data does have a status and if we look at the status of that which is actually the payment state if you hover over this in Visual Studio you can see that the payment state has a few possible values and they are always a number so it's either going to be payment pending payment received free trial or pending deferred essentially but our status here needs to be one of the subscription statuses that we set up so we do have the subscription statuses and really there's only three of them that we are interested in so we're going to have to transform these numbers into one of those three statuses so firstly let's include this and what we can do is create an array here that has the index of the status that we want for the index of the state that is going to be passed and essentially if the state is blank then we will use an additional status so this only has up to three and we can create a fourth one if this payment state is blank because if it's expired it's actually going to be blank it's not present for canceled or expired subscriptions so what we need to do now is actually add in the statuses for the matching indexes so at index zero payment is pending so the status for that should be pending and where I'm getting this pending here is back from here these are the three options we have so it's pending active or expired and when the payment state is one it means payment was received so this would be an active status when it is two it's free trial so that's also going to be an active when it is three it means pending deferred upgrade or downgrade so we're going to use pending again and then there is no there is no value four it'll just be blank so if it's not present then it would be turned into a value four and if that's the case it means it's expired and we just need an as here and that will typ cast this value as one of the subscription statuses uh I believe we just forgot the comma there but this should be working we do need a semicolon there but this setup is good we add the semicolon there and now we can actually make the call to our create or update purchase so this is going to need to be awaited and we're going to be calling the Firebase calls and then we can call create or update purchase and this simply takes the purchase data so we'll just pass in that purchase data that we just defined and that is it we can log out a note that this was successful but the most important thing we do here is return true because at this point our purchase has been verified and validated there is an error here with a missing comma so we'll need to add that in and also another missing comma here we also have an error here that product data isn't defined so we do need to include that and we can add that product data from the products file there's no more errors we do have a few warnings but that's to be expected at this point next we can do the handling of the non-subscription this is going to be very similar although a little bit shorter we're going to do the almost exact same thing here and get that response the only difference is instead of calling subscriptions here we're going to be calling products and then we're going to be changing the parameters a little bit the package name is the same but instead of subscription ID we're going to be passing product ID and the rest of that should be all right we want to do the same thing here with the try catch block so let's wrap this in the tri block here and catch the error the error is going to be very similar so we'll catch the error similarly and print the same two lines the next thing we'll do is actually check that that order ID exists and we'll do that almost exactly the same as we're doing it in the subscription for the non-subscription we don't need to do anything with that converting of the order ID it'll always be the same because there's never going to be those added characters at the end for a non-subscription so at this point we can create our purchase data and this is going to be the type non-subscription purchase and this is going to have all those base values which are the first five parameters here the order ID here is not defined though since we're not changing it so we will change this just use the response data order ID product ID will be the same the user ID will also be the same and the purch purchase date will be slightly changed in that it won't use the start time instead it uses the purchase time but everything after that is the same the last thing here is just the status and we will be doing something similar so we'll be getting that response data purchase State and for this one we can see that the purchase state of the order possible values are zero for purchased one for canceled and two for pending so this should always give us a value if it doesn't though we can default it to purchased and then we'll type cast this as the non-subscription status so we do need to convert these so if we look in our non-subscription statuses we have pinning complete and cancelled so in the value is zero that means it was purchased so we want this zero index to be complete and and the value at index one is going to be cancelled so we will make this cancelled and we use cancelled yes the third value is for pending so we'll use the pending value here all right that is good and last thing we have to do is just pass this to our creator update purchase which will happen in the exact same way that we're doing it in the subscription one and then we'll return true so if we save this it should be good now but there are a few errors so let's see what we have if you hover over this it basically tells us exactly what the issue is and it's that we are not including the type we have the type of subscription above but down here we will have the type of non-subscription so that's a good example of why typescript is better than JavaScript without typescript we wouldn't have that quick that quick check there and then also kind of show us exactly what was missing so anyway that looks good now and we no longer have any any linter errors on any of this code here so really at this point our Google Play purchase Handler is set up there's just one last thing we need to do and then we'll be able to redeploy this to test on our actual device so if we go into the index file if you remember we made the Google Play just use the app store purchase Handler as a placeholder we're going to want to change that to use the actual Google Play purchase Handler and we will need to import that up at the top as well we already did all of the heavy lifting for this so our verify purchase will now just use the Google Play purchase Handler if Google Play is the source that's passed in and if you look down here where we're actually defining that you can see you can see where we use the purchase handlers and we're going to be just getting the purchase Handler of that data source which if it was Google Play then it would just use the Google Play purchase Handler and obviously everything else would work as we would expect so this is good I know it was kind of a lot to get through and hopefully this all is making sense so the last thing we're going to do in this video is just redeploy our Firebase functions and that looks good you can see everything deployed as we would expect in the next video we'll do a test on our Android fiscal device in the last video we deployed our verification function for Android and now we can test that that is working correctly so I have this running here on my physical device we already set up all the code that's needed for verifying the purchase so you can see we have this verify purchase here and we set this up for iOS but it should work EX exactly the same so there's no code changes that actually need to be made to our flutter code base so what we will be looking for is a new purchase to show up here and that verification to be marked true so the user actually will get their inapp purchase as well so we'll Begin by just doing a decision increase So currently we have three decisions if we go to the store we can buy the five decisions here he and it looks like this did not actually work we do not have the we weren't granted the decisions so let's check out the logs here so in our logs here you can see that the current user has insufficient permissions to perform the requested operations so this can actually happen when you first set up these Keys it could take a little bit of time for that invite and that user to actually be valid so if you are seeing this error the best thing to do is actually just wait a few hours potentially up to a day and then just try it again I do believe our setup is correct so I'm just going to wait a bit and then I'll come back and try it again all right so several days have now passed and I'm going to go ahead and test this and I purchase now again on my physical device for Android so as you can see here the device is running and there are three decisions so if we go into the store here and buy the five decisions and go ahead and complete that purchase we see a bunch of logs down here if we go back we see now that the decisions have increased to eight and if we go back into the Firebase console here we can see that we have that Google Play purchase now and you can see this is several days later from when I first originally tried testing this which I believe was on the 11 maybe I don't actually think it takes that much time it's been over a week for me definitely but I think normally about 24 hours and you should definitely have it working if it wasn't already but this is definitely something that's frustrating if you don't realize it when you're trying to build this out because you might be thinking you coded something wrong or you did something wrong but actually it's just that Google takes a little bit of time to catch up so this is great our non-subscription is working on Android but we can also go ahead and test out our subscription here so if we do our unlimited month and subscribe to that we should first off see that we are now on an unlimited account so that works good the purchase was verified and then if we go over into here you'll see we have this unlimited month that was purchased and you can see that our status is active So currently both IOS and Android are validating purchases exactly how we want them to and we're essentially done with that part but there are a few few more steps that we need to set up in order to have our subscriptions expire correctly and we'll cover those in the next few videos So currently we're able to verify our purchases with the app stores but we aren't really able to fully manage our subscriptions yet so you can imagine right now if a user does subscribe to our unlimited monthly all of that will get verified and they'll get the one month of unlimited but we're doing nothing after that so currently if we were to deploy the app in its current state the user would just have to subscribe once and then they could cancel it and they would still always be on that unlimited and there's a few things we need to set up but the first thing we're going to be doing is actually listening to the app store's events so if you go into App Store connect you can see if you select your app and then click app information under General here you could find the section where you can set up the app store server notifications Google will handle this a little bit differently and we're going to cover that later but just focusing on Apple you can see that you can add a production server URL in a sandbox server URL once you actually add a URL here what will happen is Apple will send information to that URL and the information will be about the subscription so for instance if the user cancels their subscription this URL will be notified also if the subscription is renewed information will be sent about that so there are various different events that can be sent the most important ones are about when the subscription expires or when the subscription is renewed and those are the ones we're going to be focusing on now but we need to set up our backend server here to have a URL to send to and then we can actually configure our app store connect and start testing all of that so you can already see right here that these Firebase functions do have a public URL so this function for instance is accessible at this URL so following that same kind of pattern we're going to create a new Firebase function and use the URL of that Firebase function to update in our app store connect so back over in our vs code if we find our index file which is where we're defining all of our Firebase functions we currently are only exporting the verify purchase and we're going to export a new one now and this is going to be called the handle App Store server event and this is going to equal a new function which we're actually just going to Define in our app store purchase Handler So currently our app store purchase Handler has the handle subscription handle non-subscription and then all that is kind of within this logic we're going to create a new named function here called that handle server event and this is going to equal a new Firebase function which will be asynchronous and it will take that request and response so in its simplest form right here we're going to have this just return a 200 status which just means that everything worked correctly and we can also just go ahead and log out the data of the request and our functions will need to be import so if we go up to the top we can import all from functions and then we can also just Define the functions up at the very top above the class itself this is the same way we have defined the functions in the actual index itself if you remember so essentially this line here is what we want and we will need to also pull in that cloud region from our constants actually since we already have the constants we can do this all on one line there and this looks pretty good data is actually the wrong key here it's body is what we want so the request will have a body and the request will have a status and a few other things uh but this should be good for now suppose we could also just log out the request this will get built out more obviously but we just need this for now to actually get that URL set up so back in the index file we're going to be exporting this new handle App Store server event and actually we do want that to be a lowercase H and really all we want to do is now reference the new handle server event that we created in the App Store purchase Handler so we're can to access the App Store purchase Handler from our purchase handlers that are defined right here and we'll just find the one for the App Store so this will be the App Store purchase Handler and then we can can just call handle server event on it we do need to actually typ cast this as the App Store purchase Handler and then we can add a semicolon at the end there this should be working we're getting an error I think because of the linter max line length but if we save all this this looks how we're going to want it and we can now redeploy our Firebase function so that we get that URL all right and our deploy is complete so if we go back into our Firebase console here and reload the page we'll see now we have this handle App Store server event and what we're most interested in right now is this URL so we'll copy this URL and we'll go into our app store server notifications and click set up URL for the sandbox URL and then go ahead and paste the URL in there we are going to be using the version two notifications and then hit save depending on how you end up setting this up you might have a Firebase function for your sandbox and your production if that's the case you can have these separately uh if they are the same when you are deploying you would just use the same URL there then but this is good and saved now we can actually test this by getting some data by adding a new subscription from our physical device firstly I'm going to go back to my test sandbox users and just clear out any of the purchase history that may exist and now I can run my app on my physical device all right so on the physical device here when we actually buy the subscription we're going to be looking to see if the handle App Store server event is triggered now we don't necessarily expect this to be triggered on the first purchase but we are going to be most interested in looking that it is triggered when the subscription renews one thing that we do want to make sure is in our app store connect when we go to that test user account you can choose the subscription renewal rate and this is just the amount of time that will laps between the subscription for these test users so I like to set it to the lowest one when testing like this because is you can just get quicker feedback so basically this means that even though I'm going to be doing a one month subscription it will renew in 3 minutes so we can go ahead and buy that inapp purchase now so the unlimited monthly and you can see now we are on that unlimited plan if we check the log specifically for our app store server event we can see that nothing was actually loged when we bought this na app purchase and that's okay we are most interested as I said in looking that something is logged once the subscription is renewed so we're going to need to wait about 3 minutes now all right and it looks like we didn't actually even need to wait the full 3 minutes the store was just slightly delayed so you can see right here it is printing out that console log of the requests and that is just an object and then also the request body is another object so we're not getting the best printing here but it is definitely hitting that that new function that we created and this event here would have been for the actual original purchase event so I am going to let a little bit of time pass and you can see that this is going to get logged a second time basically 3 minutes after this one was originally logged all right yes and you can see there is another one logged here with that request and the request body if you look closely at the times here the 3 minutes is actually after the original purchase which was at 10:46 so this is 3 minutes after that the first one just had about a minute delay in its original logging but this is exactly what we would expect and all of the setup is good in the next video we're going to set up a way to run these functions locally so we can test them easier all right so we're able to view these logs and kind of test using the logs here but there's actually a much simpler way we can test this kind of functionality and that's by running our Firebase function locally which will allow us to actually send requests to the function locally and see what's logged out right within our terminal and not have to rely on the actual Firebase function log that's what we're going to be setting up in this video the first thing we're going to do though is figure out what this request body actually is so that we can use that when we are testing locally we are going to need data to send to our local Firebase function that looks the same and is formatted structurally the same as App Store connect is sending so if we actually go over into some of the Apple documentation and I'll link this below you can see what this response body is going to look like but just reading through this you're not going to be able to come up with actual test data that you can use in your local Firebase function so that's why it's important that we actually can get the body printed out here and we almost have that set up except the way I printed this is incorrect so you can see we're just printing the request in the request body here but what we need to do is call Json stringify and then pass it through request and request body this will actually convert those objects to Strings and then allow us to actually view it in the logs so this is what that is going to look like so you can just save this and then redeploy the Firebase function if you are able to make this change before essentially before 30 minutes pass you should be able to see the logs update without having actually create a new subscription and that is because your test subscription will renew 10 times before it expires so basically you have 30 minutes because every 3 minutes the subscription will renew if you aren't watching these back to back or that 30 minutes has already elapsed you can just go ahead and do another test version of your inapp purchase subscription and then you should see the body is actually printing out the full body and not just the object object but while all that is running we can actually set up this Firebase function to run locally and the first thing we need to make sure is that we have our Firebase tools set up so we can run npm install Firebase tools and I believe I already have this set up but running it again will not hurt anything so I'll just go ahead and run it again and basically that's all you need to only run the functions we are going to run Firebase emulator start only functions and this will run the Firebase functions locally I'll definitely link down below to more information on running Firebase functions locally so you can see it gives us this nice URL here for our local Firebase function so if we go to that URL in the browser you're going to see now we have this log here and this is going to mirror what is in the logs of our Firebase functions logs on production which are what we see right here if you go to the overview tab here you can see all the other Firebase services that you could potentially run locally the only one currently that is on is the Firebase functions emulator so that is really all we need so that's good now what this means is we're able to use this URL here for our function instead of the actual production URL so this is basically the local version of that Firebase function URL what we really want to be able to do is send the same type of data that apple is sending to our production Firebase URL or even our sandbox Firebase URL and we want to be able to send these App Store server events locally whenever we want so that we can get them testing and run through the logs we don't want to have to wait every 3 minutes for the subscription to renew we want to be able to send one every minute or really we just want to be able to send one whenever and not have to worry about going into our app creating that subscription again and doing all that test stuff that we already did and to accomplish this there are a few ways you can do it but the easiest one and the one that I like the best is using Postman so we're going to copy this URL here and then we can open up Postman and what Postman allows you to do is just send any type of post request or even a get request really any type of request this is a very powerful tool but all we're going to be using it for is sending a post request to our test URL here the cool part of this is it allows you to add all this extra post information so we need to fill out the post body because that is what apple is going to be sending to our actual handle App Store server event is going to have a body associated with it which is going to have quite a bit of data so that data is actually what we're trying to figure out based on what is being printed here so if we can find what the body of that data looks like then we can basically just put that into this test request that we're about to make if you look through some of these logs here we're actually getting some errors it's actually because we're calling Json stringify on the whole request itself on this line and this is actually just causing errors the body is Json the request itself has multiple components to it and that can't easily be converted to Json so just remove that line completely or just don't do the string of five but without the string fi it's kind of useless we'll just remove that and then redeploy this all right and after fixing that you can see we do actually get the request body and it is now showing us what that request body is if we already to click into this it's quite long actually but we can copy the whole thing and we want to copy that and paste it into the body of our Postman request that we're about to make so the format of this is going to be raw and we'll change that to Json and then if you paste that in you can see it's this very long just signed payload is the request body you want to make sure you remove the request body text at the beginning and just actually include what the value of that request body is also be sure to look at the end and make sure to remove any extra characters there but this request body is an example of what apple is going to send us when some subscription event is triggered in our app so what we can do now is actually just send this post request and we can look down here and we'll see that nothing really happened but if we go into now our console that is running our local Firebase function you can see this printed out the request body as well and it has that signed payload exactly that request body and this is actually exactly what we expect because what's happening right now is we're sending a request to this local instance of our handle App Store server event and we're passing it this information as the body so when that function gets hit it's going to essentially pass that off to our handle server event which is right here and then that's just going to print this out to demonstrate the value of having this locally set up we can try and log another message here and this will be just called New Message so without the local functions we'd essentially have to just redeploy this whole function and then in theory we could still use this setup but send it to our actual function so that would be a little bit quicker but still the deploy takes a little bit of time you might be thinking now we could just hit send and it would work but unfortunately that is isn't quite going to be enough because the local functions code here has not been rebuilt so what we need to do is stop the local function and once we stop that we need to actually make sure we're in the functions directory now then we can run an in PM run build and this is going to recompile the code with that change that we just made and then from here we can rerun the emulator and now if we go back and hit the send here we would expect to see that new message logged as well as the old one and you can see it does say the new message there so this is good and we can speed this up even further by changing our configuration to automatically recompile so if we go into the package.json here you can find where our scripts for build are and instead of having it just TSC we can add a-w which will recompile or essentially rebuild this on any file changes so there's two things we need to do now firstly we will rerun the npm Run build and you'll see now when this is running it's a little bit different it just says that it started and it's now in watch mode so basically this is running and if we uncomment this now and save it you're going to see over here that file changes are detected and it's going to start doing the compiling now if you do that it will run in the background and then you can start the emulator again now we'll no longer need to stop the emulator so we can go back here and if we were to for instance uncomment the request body maybe we'll add three exclamation points now and we'll hit send when we go into the logs we'll see it is the three exclamation points if we go ahead and uncomment this now and go hit send again and we come in here you'll see now it is running essentially in real time so the three exclamation points and the request body were both run this is definitely the best setup when you need to do some testing in terms of a lot of logging so you'll just have this build running and listening and then you'll also have the local Firebase functions running and in the next video we're going to be discussing this actual assed payload and we're going to be doing a lot of logging out to see actually what this is and how we can actually use this to extract useful data so this is all good and now we are set up to be running all this locally and it's going to make our development time so much faster we currently have our functions set up for handle server event obviously this is not doing anything except printing out the body of the request and we're able to now make these test requests which you can see one logged out here if we just clear this you can see we can make this test request to our local Firebase function that we just set up and we have this sign payload which is going to be what apple is going to send us but this sign payload as it is is nothing useful so if we hit send here in this data here in the signed payload if you go back to the documentation this signed payload is in the form of a Json web signature or a jws format and essentially what this is is just encrypted data so if you look it is a base 64 URL encoded and B basically what we need to do is decode this to get the actual data that is inside of it so if you're interested in why Apple sends the information like this you can read through this documentation a little bit more and look a bit more into jws format but overall it is kind of just a way to standardize and encrypt to some degree the data that's coming in so they just have to always send this block of text which is always essentially going to be the same size and the data within it can change but it's not easily read without decoding It Anyway let's go ahead and decode this so that we can actually get the useful data that's inside of it and we're going to be doing this with a new package and the package is JWT dcode and we're going to install this in our package.json file so if we find the main dependencies here we can just add a new one and it's going to be the JWT dcode and I'll be using the version 3.1.2 and then we can run in PM install however we do need to make sure we're in the functions directory and then we can run npm install and you can see it will add that one package we can clear out of that and now we want to go back into the code here so we can use that new package to decode this request body firstly let's just go ahead and remove these logs here and the first thing we're going to do is actually Define what the signed payload is so we're going to create a new constant here and this will be the decoded body and this will have a type of the signed payload which is going to be part of the Apple receipt verifier this is going to equal now the decoded version of that body signed payload so for this we can use this new package and there is a function JWT decode and this is going to take an encoded JWT format which currently exists in our sign payload within the body so if we remember back when we're printing this out we have the body here and this is all the body but the body is actually a key value pair and the key is the sign payload and then the the value of that key is actually the JWT value that we are looking for so to get that we can just do the request body do signed payload and this signed payload here needs to match up with the exact case of this sign payload here because that's actually what we're going to be accessing so make sure it's the lowercase and then the uppercase P that is good we can now print out this so we can see what it looks like and we will add that Json stringify around it uh a couple errors here to clean up firstly we will need to import this package and that line will look like this next we can remove that space there and lastly the signed payload is not actually a type that has been created yet so the Apple receipt verifier actually does not have a signed payload type associated with it but we can create an interface that will create that signed payload but for now we can just not have the type there and print this out and see what that looks like then we can create the actual type for this signed payload if we save this and then go back into our Cloud functions here just going to clear this and rerun it and then we can send another test and now we can see what is printed out we get the sign payload and now this sign payload is different data so you can see it is a key value pair and it has a several different values in it so we have notification type did renew notification uid and it has the uid then it has data that's another whole key value pair and really you can see within here again there's another jws token so that is the sign transaction info and this information is also going to need to be decoded to create this type now we can create a new interface up here which is kind of what we already did with our Apple receipt verifier and we created the purchased products interface and we're going to create another interface now for the sign payload and there's basically two elements that we're interested in from this signed payload the first one is going to be the notification type so you can see that one is right here and this is going to tell us what kind of alert we're getting from the App Store so this one is a did read new type and other types include like expiring or canceled and those would all be part of this notification type so we're interested in this notification type and then we're also interested in all of this data here we don't really care too much right now about the notification uid but if you did want to use that for anything you could include that in this interface here as well I will just be using the notification type and that will be a string and then I'll also be using that data and this is another key value pair so the key is going to be a string and the value is also a string so that's good we have this signed payload type the next thing we can do is is actually go ahead and get this sign transaction info and decode this JWT block as well this is where actually the most useful data is the rest of this is not as useful so to do this is kind of a similar thing we're just going to create another constant and this time we'll call it assigned info and this one will have a new custom type as well that we'll create but for now we can just leave this blank and we'll see what the structure looks like first but this would be another JW T decode and we're going to decode the actual decoded body here and then we're going to get that data element on it and then within that data element we are going to get that sign transaction info so we're basically going to take this key value pair dig into the data here and then within that data we're going to dig into that sign transaction info so we actually end up just getting this block here and this will be good so now let let's print the signed info instead of the signed payload and if we just clear this and send another event you can see this is a lot shorter but we have our sign info and it has the transaction ID and the original transaction ID these could be different because we have a subscription that the original transaction ID would have been that original subscription and then the transaction ID would be this current renewal of the subscription uh there's a few other things in here that are definitely useful we have the actual product ID here and we have the purchase date original purchase date expiry date within this here there's really three parameters that we're most interested in and that's going to be the product ID the expiry date which is kind of getting cut onto two lines here and then also the original transaction ID so we're going to create that new custom type with just those three elements in it and it will be an extension of the Apple receipt verifier so we'll do a similar thing extending the Apple receipt verifier and then we'll just call this the sign transaction info so we will need to create this interface now and as I said there's just the three elements that we're interested in so it's going to be product ID expires date and the original transaction ID and all three of these will be a string so this is a good start but we have some data now from the decoded body that we're going to be using directly and then we also have some data from the signed info that we're going to be using directly so just for the sake of organizing we're going to create a new key value pair of just all the information that we are going to use and have it stored as one key value pair so we'll call this the event data first one is going to be the notification type and that is going to be exactly let's actually comment this out and run this for both of these so we can see both of them at the same time I'll clear that and send another event so we can see in our signed payload we are going to just pull the notification type so we'll uncomment this now so notification type is going to be from the decoded body and then we'll pull that notification type next we're going to set the product ID and this product ID is going to be part of our signed info then want set the expires date and that also is just going to be part of the signed info and lastly we'll do the original transaction ID which is also part of that signed info so this event data isn't 100% necessary to set up you could just directly use these values here but it does end up being a little bit cleaner to code out later and if you did need to change any of this stuff around it's kind of easier to have it in one place to change here so basically these four parameters are all we're going to need to look at to go and find a subscription and then update it accordingly so I'm going to comment out these two logs here and really all we're interested in doing in this handle server event is really expiring or reactivating our subscriptions so there's going to be kind of two parts to this firstly we're going to use the product ID to find the product data and then then determine if it's a subscription or not if this event was from a non-subscription purchase we don't really need to do anything here because our validator is going to handle that already and there's nothing that needs to actually happen from this handle server event if this is a non-subscription purchase firstly we're going to get the product data and we're going to be able to get the product data from the product ID and if you remember our product data is just an interface here that has the product ID and the type so if we go back we can find the product data actually using this product data map and we'll just set this to a new variable and we're going to need to pass this in the product ID so we can get that from our event data now now that we have our product data defined we're first going to do a quick check to make sure that this product ID does exist meaning that this event data product ID is one that we have configured within our product data because you can imagine if we did add and have purchase but we didn't configure this this lookup would just return nothing so we do want to handle that here and we can do this with a quick if statement and just check if the product data is blank so if there is no product data then we're going to just log out an error and return a 403 status and instead of sending the 200 status we're going to send a 403 status which is a more appropriate status for this because this would be considered a forbidden call where 200 is a successful call uh but after this we will just return out of this function completely so that check is good now we know our product data is valid but we want to check if it is a type of subscription because if it's not a subscription there's also really nothing we need to do so if the product data type is equal to the subscription all right so we've already covered quite a bit in this video we are decoding this information now from Apple and we're able to actually find the purchase data and determine if it's a subscription in the next video we're actually going to handle these subscriptions and update the users information based on the information that apple is sending us all right so currently we are decoding our Apple data let's go ahead and print out this event data just so it'll be a little bit easier to see what we want to do all right we'll comment this part out down here and we will clear this and then go ahead and send another event so we can see what our event data is right here we have the notification type we have that product ID we have the expires date and the original transaction ID so this is all the information that we need to make this update really what is most important here is that we are updating this expiry date for our subscription which we can find with this transaction ID so you can see here that this test transaction ID is the one ending in 6609 so if we go back into our Firebase console we can find that App Store purchase here and all of that this contains right now is that original purchase data so you can see that the original purchase date was the 27th at 10:45 and the original expiry date was the same day 3 minutes later so all that is expected however this event here the timestamp on the expiry date now is is going to be later than this original expired date but our system is not kept in sync with that and our system still thinks that the expired date is that original expiry date even though the user renewed their subscription without keeping this in sync we can't currently mark this as expired based on this expiry date because this expiry date is no longer accurate so really what all of this is going to be doing is making sure that our expiry date for the user is accurate and then as long as this expiry date is kept accurate it will allow us to run a function every hour or every day and just check if any of these active subscriptions have expired and if they have then we can mark them expired so that part will happen later but currently we will be getting this expiry date to be in sync so we already started writing this if statement here and we are interested in only the subscription products this will be a subscription product you can already tell because it's the unlimited YT monthly so this block here will be hit and all we really want to do here is update this purchase so we need to find this purchase and then update it and we're going to do this in our Firebase calls so our Firebase calls we only currently have one call for create or update a purchase and this is what we're using when we're validating to originally create this document right here but we're not going to be creating a document we're only going to be updating the document because it will already exist from when that original subscription occurred so we'll create a new asynchronous function here and it will just be called update purchase and this is just going to return a void but it has to be in a promise because this is a synchronous so there will be a few parameters that this takes mainly the purchase data and that purchase data is going to be the source and the order ID we're going to need the source and Order ID both to find the document so the source is going to be basically the beginning of the document name and the order ID is the second part of the document we are going to set this up now so it will work with Apple and Google so we will need the inapp purchase source which is just the IAP source and the second element in the purchase data is going to be the order ID and that will just be a string this purchase data is kind of a custom object that we're creating right now it's going to have these two elements and then you can see up here here how our purchase data was just a purchase and we will be still including areas of the purchase but we aren't going to require a full purchase to be passed in so we can just call the purchase here and we will use this and which is going to essentially concatenate the purchase parameters with these other two custom parameters that we just added and we can say that this purchase is just a partial which means that we're going to be requiring at least some information from this purchase or we will allow some information from that purchase so we can break this down to a second line and now we're just going to do kind of what we did up here we're going to create we're going to find this purchase ID and it will be exactly the same as it was defined up here we also can Define this purchases collection and then in a very similar way we're going to get that purchase document so basically these first three lines are exactly the same and this last slide will be a little bit different instead of setting the purchase data we're just going to be updating it so this will be that await call and then we're going to await the purchase and we'll call update on it and then we can pass this the purchase data so this is good we can call update purchase here but what this isn't going to do is actually update our user so imagine now in this example that for instance this user was expired but then they renewed their subscription and now this got updated to a later date and the status also got updated but they were already marked expired and the user's unlimited was already removed from their actual user object I believe this user would have been this user so imagine that unlimited was already marked false we want to make sure that if we're updating this purchase to be active that we're also updating the user object to have that inapp purchase and also we want to make sure the opposite is happening too so you could imagine that if this update was actually expiring the user and marking them expired we would want to remove that access from the user as well both of those cases can happen in one function where we just check what the status of that document is and then update the user accordingly let's create that function right down here and we'll just call this update user with purchase this will take one parameter which is going to be the document reference which we will just call the purchase reference and the type of this is going to be a firestore document reference and this is not going to return anything but since it's asynchronous it will be a promise so firstly we're going to get this document so we have the purchase reference so we can actually just get the document based on that reference and we'll set this to a new variable called purchase Doc and we can just get this purchase reference so purchase Doc is essentially going to be this document here in this example and now we want to just make sure we have a user ID on here because if there's no user ID there's really nothing we can do but all of these should have a user ID and then we're going to use that user ID to go find that user document and update them so firstly we'll just check if the purchase document has user ID and the easiest way to do that is just to check if it's not undefined so we can check the type of that field on the document so we're going to be looking at this purchase document and then we're going to get its data and then we're going to be checking for that user ID parameter which is this right here and if that is not equal to undefined then that means it does exist and we have a user that we can go look up so now we can Define that user here and this will be the user reference and it's going to Simply equal this.f firestore do collection and we're going to be looking in that users's collection and then we're going to look for the document which has the ID of the user ID which basically is the same logic right there so we're going to be looking for that user ID that is in our purchase reference document and this is going to give us the actual user document so this user ref here is going to be this user is basically going to be this document here so there's nothing that we actually need to do if the purchase reference here is not related to a subscription so back what we were looking at you can see this one is a subscription but this one for instance is and this one's also a subscription this one for instance is not a subscription so if the document that we were referencing here and we looked up the user was related to this document there's nothing we need to do for the user because the validator is already going to handle giving the user their purchase there's nothing we need to do in this update user with purchase because this is only ever going to be called when we're updating subscriptions all of that to say we can just skip the non-subscription purchase types so the easiest way to do this and we can do it in one line is to just check if it's not one of these subscription purchase types so the ones that we have if we go back to our products here we have the unlimited YT monthly and we also have the unlimited YT yearly so if it's not one of these two types then we're going to return so to check if it's one of these two we basically put them in an array here and then we can say if it includes and then we're going to be passing it the purchase document data of the product ID so all this is saying is is the product ID one of these two and if it's not which is what this is going this exclamation point is right here so if it's not one of these two then what we want to do is return there are a few ways you could write this this is almost like a backwards way if you think about it because you're basically saying if it's not one of these then return so really the only time you're going to get down here is if it is one of these two so if it is one of these two it means we're dealing with a subscription and we want to check if the status is active and if that's true we want to update unlimited to being true and if it's not active then we want to update unlimited to being false so we're going to check if the purchase data status so we can pull the purchase data like that and then call status on it because the stus will be this right here but on a subscription it's either going to be active or expired so we can check if the status here is equal to active and if that's the case then we want to get this user reference and we want to update the unlimited parameter to be true and then if not we can do an else if here and we're going to be basically checking the same thing but we're going to be checking if this status is expired and if that's the case we're going to still update this user reference from for unlimited but we're going to set it equal to false now this logic could change depending on how you're setting up your Ina purchases and your subscriptions but the concept here is that you want to make sure your user object is still in sync with your purchase so if this purchase is being updated essentially renewed and it's still active or it might be changed from expired to active you want to make sure that if any of these statuses change that the user is also changed because just changing this here is not going to change the user and that means that this could be accurate but if the user is not accurate they're not going to experience the features in the app of the inapp purchase that they just purchased so this is a very important part and we just need to tap that in extra there we're going to call this update user with purchase now from our update purchase and we'll just be passing this in the purchase because this will actually be a purchase reference and we're also going to call this when we create or update the purchase kind of for the same reason it won't be needed when we're creating but when we do the update portion of it we would want to basically do this double check and make sure that if the status was changeed to active just to make sure that unlimited is true and if it was changed to expired make sure unlimited is false so this is an easy way to do that and you can add that in there and that's it now we can just call this update purchase now back from our app store purchase Handler so we're going to do this in a TR catch block and we can do the error handling first so if for whatever reason this doesn't work we can just say that we were not able to update the purchase and we'll also log out the event data just so that we can see a little bit better what is going on here but now we actually need to make this call so we're going to be calling that Firebase calls update purchase so this will be an await call we can call this. Firebase call because we did already set this up earlier when we were handling our validation so we're calling Firebase calls and then we want to just call update purchase and now we need to pass in the parameters so update purchase is going to take the inapp purchase source which in the App Store purchase Handler is always going to be the App Store next we'll have the order ID which is actually going to be that original transaction ID because if you remember when we were looking in the logs here original transaction ID is just the original order ID and it's very important that we use the original transaction ID and not the current transaction ID because we won't be able to find in our database the original transaction if we're using the current transaction so anyway we'll use event data. original transaction ID next we can pass in any parameters that we want to update from our purchase itself so if we go back here we can look and really we can pass in any of these the ones we're going to be passing in are status and expiry date so let's do expiry date first and for this we do have the expiry date set up here but it is in milliseconds so we will need to do that conversion which is a time stamp for milliseconds and then we will need to parse the integer and we'll give it that radius which will just be 10 the last thing is going to be the status and the status for this can be one of two things it's either going to be expired or active we're not really interested in the pending status here because if the event was sent it means something either expired or something was either cancelled and we can figure out if this is expired or active all based on that expiry date and the expiry date is this exact date right up here but we can just check if that expiry date is before now or after now so we're going to be looking at the date of right now and then we're going to see if that is greater than or equal to the expiry date and we can just pull this parse in here and if the current date is greater than the expiry date then that means that this is expired so we're going to move this down to the next line but we will set this to expired and if not then we'll set this to active and we'll add a semicolon here and we don't need that extra space there now time stamp just needs to be imported so if we go back up we can import timestamp from the fir store timestamp so yes currently this looks good so there are a few ways you can test this and I'm going to show you one but I would suggest you can you do both one way is to redeploy this Firebase function and then go create a new subscription and then watch that this gets updated every 3 minutes I basically already show you how to do that in past videos so you can go ahead and do that but locally also since we are using this original transaction ID that matches up with one of these transaction IDs here if we do rerun this locally we would expect this date to update now it is going to update to whatever that expiry time was when this event occurred and all of this is kind of test data from the past so it's kind of going to be a onetime thing that we can update it but we will try that right now so you can see right now the expiry date is the 27th at 1048 so if we go ahead and rerun this locally we're going to expect that that got updated to a later date and you can see it did it got updated to December 27th at 11:15 so this is now expiring later and all of this setup is good and that means that this is going to be kept in sync with our app store events and again this is very very important because if you have a subscription you definitely need to be managing the subscription so that your users are getting what they purchased then also their access is being removed when they stop paying you so we just set up everything for apple and we're able to listen to those store events we're going to set up a very similar thing for Google Play although it works slightly differently than how Apple Works the concept is the same that we're going to be getting notifications from Google and and they're going to be telling us when a user updated their subscription or their subscription expired but they're going to do it in a slightly different way and that they use a pub sub topic really all that means is we can subscribe to some topic that we Define within Google console and basically we're telling Google we want you to alert us whenever any of these events related to purchases are made so we're going to start by going over to console. goole.com and we're going to find the pub subtopics and I'll have this direct link below but we're going to create a new topic here and you can kind of see what this is going to be doing so a topic forwards messages from Publishers to subscribers so in this case we will be a subscriber and Google will be the publisher so we can call this whatever we like and I'll call this decider YT purchases because all of this information will be related to the inapp purchases from here we can just hit create topic and once that's created it will bring us to this page and we do have a few things that we still need to do firstly we can go back in our code and add this as a constant that we can use later so back in our code find that constants file and we're going to export a new constant and this is going to be called the Google Play Pub sub topic and we want that to equal that exact name that we just gave it in the Google console so that's good and we will use that later now back in the Google console we do need to add some permissions to this so you can see over here we have the permissions and if you look at the permissions some of them will be inherited from your overall Google account but we are going to need to add a new one and the one we are going to need to add is this Google Play developer notifications and it's going to be at system. gservices account.com I'll link this down below as well and then for the role of this we need to set it to the pub sub rooll and make sure you do the pub sub publisher because this is going to be publishing so we can hit save there now that we have this set up we actually need to tell our app to use this publisher so we're going to need to open up the Google Play console now and go into our app and then scroll down to monetization setup and you can see right here we have the real time developer notifications and this is going to allow us to receive those event notifications from Google and all we need to do is just paste in the pub sub topic here which if we go back in here we can find that topic name and just copy this and then that is going to be what we paste right in here and then we can go ahead and hit save changes on that we will come back and use this test notification but we don't really have anything set up yet to listen to this Pub subtopic so that's what we can begin setting up now and we're going to do this back in the code here so just like we did with the iOS in the index file we're going to create a new function for the Google Play Store and this is going to be called handle Play Store server event and instead of using the App Store purchase Handler we're going to be using the Google Play purchase Handler and we will create a new handle server event in the Google Play purchase Handler so we can just save this and then go back to the Google Play purchase Handler and all the way at the bottom let's create a new purchase Handler here and this will just be called the handle server event and this is just going to be a Firebase function so the type of Firebase function that we're going to do here is a pub sub so this is going to be a little bit different than any of the other F rase functions we've created and the pub sub will take a topic and that topic is going to be from that constant that we just defined so can copy that over here we will need to import that in with our other constants and then with this we're going to be calling onp publish this will be an asynchronous call which will take a message and for now we can just log out the message and we do need to include the functions here so kind of how we did in iOS we will need to include functions and then Define it so if you scroll up there you can see where we defined functions with our Cloud region we're going to kind of do exactly the same thing here and we will also need to include the functions and then we're also going to need to include the cloud region from our constants but that should be good functions are included now this needs to be double tabbed in but this looks good so what we can do is actually just deploy this function now and then we'll be able to test the pub sub is working so we'll just go ahead and deploy this and while that's running I will kind of explain a little bit of what's going to be going on here so we're going to have this new function here which is essentially going to be listening to this Pub sub topic and it's going to be listening for the unpublished trigger so basically what this means is it's going to be getting the message or the data that is sent to this topic and this over here in our app topic is what's actually going to be publishing to the same exact Pub sub stream pretty much what's going to be happening is the app here is going to be publishing stuff to this Pub subtopic and then over here in this function in this backend Cloud function it's going to be listening to that same Pub subtopic and basically getting the message these aren't directly communicating so it's not like it's not like apple where we're directly sending to our Firebase function but what is happening is that Pub sub topic that we set up here is kind of the middleman that everything is going to go through so the app is going to publish to that topic here and then that topic is also going to be listened over here in our Firebase function ultimately the outcome is the same as what we set up with apple but the configuration is just slightly different so one thing to not here because because we changed the way our build is using that W to watch for any changes we're no longer able to accurately deploy so we're going to need to change that back so that we can deploy this and we can just do that in our package Json and remove the w here the W again is useful when testing Firebase functions locally so we're going to clear that and then run Firebase deploy again all right and that's completed so you can see we now have three Firebase functions and if you go back into the console you can also see the new Firebase function for the handle Play Store server event so now we can go back into here and send a test notification so we go back into the Firebase console here we should be able to see that in the logs so if we click on The View logs for that specific handle Play Store server event once that loads up we would expect to see our test information logged out here and you can see there's actually nothing logged there if we go back into our setup here you may have already caught this error but this is not my project ID so the problem is I created this in the wrong project so if you've been following along you're probably fine because you probably created this in the correct project but I did not so I'm going to go ahead and recreate this in the correct project and then just update this URL here everything would be the same it's just this is not the correct project and I need to be using my actual decider YT project so I'm going to go ahead and switch that out now all right so I recreated everything exactly the same the only difference is I'm now on the correct project so you can see this topic name has changed to have the correct project ID there and the topic name is exactly the same and I gave it that exact same permission that I showed before so we will go back in here and just update this and then save it all right so that's now correct there is also one other issue you might notice here in these functions the trigger here is a request for all of these and that's actually incorrect for our handle Play Store service we would expect this to be a pub sub if we go back into our code here you may have already spotted the issue but we are using the wrong purchase Handler here so we need this to be the Google Play the purchase Handler should be the Google Play purchase Handler as a Google Play purchase Handler instead we were using the App Store purchase Handler that's why it was not actually using this server event and we're getting that lric error there so if we save this now we will need to redeploy so you can see now when you try and redeploy this you actually can't change it from an https function to a background triggered function so we will need to delete this function and then redeploy you can delete it right here in the console and just go ahead and delete that and now we can go ahead and redeploy all right and when that finishes deploying if you go back into to the Firebase console and refresh you can see now that this is deployed as a pub subtopic publisher and that is exactly what we would expect so after those updates if we go back in here and send a test notification we now would expect to see that logged out over here so if we view the logs of this you can see here that there was a log and it starts with the Play Store server event it will log out that message with the object which is all that we have currently in the function itself so the function was definitely hit so that is good this is now configured to be getting updates from the Google Play Store so the next step will be to update this function to handle those purchase events all right so currently we have our handle server event set up for the Google Play purchase Handler we are able to listen to the store events with this Pub sub the rest of this setup is going to include actually modifying our handle non-subscription and handle subscription functions to actually work for the pubsub topic so what that's going to mean is we'll be able to pass in the product data and the token to either the handle nons subscription or handle subscription and then just let the logic that we already wrote here update that now one thing that we will need to adjust for is this user ID because there will be no user ID when the pub sub is updating this whereas when we call this directly from our validation we do have a user ID so that's something where we're going to have to refactor but first we can kind of build out all the code here that calls those two functions and then we can modify those two functions so we're going to start by just removing this log message and the first thing we're going to need to do is actually create a few types that will end up becoming our Google Play billing event and then we're going to Define that Google Play building event using the message data that is passed in this is kind of boiler plate code that I'm not going to type all out and I will link this below as well but I could explain this real quickly so we're basically going to have four new types we're going to have the Google Play onetime product notification and then the Google Play subscription notification and then we also have this type for Google Play test notification and then all of those are going to be kind of used in this one larger Google Play billing event which is what we're ultimately going to use throughout the rest of this handle server event we do have this Google Play event now and we want to set that with the message data so this is going to be wrapped in a try catch block and what we're going to try here is to set this event and we're going to set it equal to this line here which is going to parse out the message data here so the message does have a data attribute on it and we're going to be taking that message data and we're going to be using this buffer and then encoding it in a base 64 but then ultimately parsing all of that back into a string that is of the asky type it's not 100% necessary NE to understand this completely but do understand what this is doing is taking this message data and making it into one of these Google Play billing events which is going to have all of these types up here so a lot of these keys are going to match up directly with the keys inside of the data object already so that should be good we will catch any errors here and we're going to simply just log out that we could not parse the Google Play billing information and then we'll just return from this function so at this point we will have our event what we will want to do here is Skip any test notifications although right now we are going to be using test notifications so we're not going to skip them but we will write this out now and then we'll comment it out so we can uncomment it later so that test notifications parameter which we do have set up will be what we're going to look at and if that is present then that means that this notification is a test and we can simply return from the function here again I am going to comment this out because we do want these test notifications to actually go through currently because we are going to be testing with those test notifications next we're going to set three variables and we're going to be getting these three variables which are the purchase token the subscription ID and the skew and we're going to be getting them from the subscription notification or the onetime product notification and since those are already going to be part of the Google Play billing event that means they are already available in our event here so what we can do is create a new constant here and we're going to be pulling out values essentially from this object here we're really going to be pulling out values from these Associated objects down here so what we're going to be pulling out are the purchase token and it is important that these names match up exactly so we'll pull the purchase token and then we're also going to pull the subscription ID and then lastly we're going to be pulling the skew so you'll notice that the subscription ID is on the subscription notification and the skew is on the onetime product notification so it would never be the case that you will get both a subscription ID and a skew but that's okay this will be this will still work as we want we're going to be extracting these from these two custom types and these two types are available through the event at these two keys so we can combine the values of these two keys together with the triple dot notation next we can get the product data and we'll be able to get the product data by either using the subscription ID or the skew both of those will be the product ID so we'll set the product data using our product data map and we're going to be either taking the subscription ID however if that does not exist then we will be taking the skew and just like we checked in the iOS version if this product data returns nothing we are going to just return from this so if the product data is blank then we're going to return now we can get our notification type and that is either going to be subscription or non-subscription so we'll set this as a new constant and we'll call it the notification type and this will equal subscription if the subscription ID is present so subscription ID is present then this will be a subscription if the subscription ID is not present but the skew is present then this will be a non-subscription and in the rare case that neither of those are present then this will just be null it's very unlikely and almost impossible that we don't have either a subscription ID or a skew so it's very rare that this would ever be null however if that is the case we do want to just return to check for this we basically can just check if that product data type which should be either a subscription or a non-subscription equals this notification type and this is kind of just a last quick check to make sure sure that we are getting the same type that we would expect so we can look at the product data's type and we're really interested if it's not equal to whatever we're having defined here as the notification type and if that's the case then we would return because really this should be the same the last thing we're going to do here is just call our handlers that we already have set up so either handle non-subscription or handle subscription and we're going to call those based on this notification type we can use either an if else statement or we can use a switch statement and we're going to be switching on the notification type so there's two cases that this can have first case is going to be that subscription and if we do have a subscription we're going to await the call to the handle subscription and here's where we will have to make modifications to handle subscription the first parameter that this takes is going to be the user ID which is going to be null right now because when this is called from the handle server event we will not have a user ID the second parameter that this takes is going to be the product data and then finally it will be the token so the product data we do already have defined here we can just pass that in and then lastly the token we also have defined and that's going to be that purchase token that we pulled out up here so we can put that in as well and if this gets hit we will break out of this case statement very similarly we'll create the other case which is for the non-subscription and actually this is incorrect this one above is for the subscription because we're handling the subscription and then this one for the non-subscription we will be handling very similarly I'll just copy this and instead of handling subscription it will be handling nons subscription although the parameters that we pass will be the same so this is good but we do need to update our handle subscription and handle non-subscription to work without a user idid so we can do this for the handle subscription first first what we need to do is make this user ID either a string or null so that makes this optional so now null can be passed into here but then we need to look at where we're using this user ID within the handle subscription so if you go down you can see the user ID right here is being passed as null on line 52 but that is not going to work because our subscription purchase is expecting that to actually exist so what we're going to do is by default remove this user ID and then we will add it back in if there is a user ID so you can see that errors this out and that's because this subscription purchase type is expecting that user ID but we can get around this by omitting the user ID from this type so if we use the omit keyword here we're basically able to say we want to take this subscription purchase type but omit this one parameter and that parameter will be defined here and that's again going to be that user ID so now essentially this purchase data type is a subscription purchase but minus the user idid and all of that is good but before we make this call to Firebase we're going to actually check if the user ID is present and if the user ID is present then we will add it back in so we can check now if the user ID exists and if it does we're going to add that back into our purchase data so we will be taking this line right here but instead of just passing purchase data we're going to pass purchase data plus the user ID we can break this down to the next line but it is pretty simp simple we have to wrap this in the curly braces because it will be creating a new key value pair and we're going to be taking all the values within purchase data and then we're going to be joining the user ID and we will want to actually cast this as a purchase which I don't believe we included so up at the top here where we're including from our Firebase calls we can also include the purchase and if you look at the Firebase calls we have that generic purchase which is either a subscription purchase or a non-subscription purchase so we'll cast this as a purchase and then we're also going to have to cast our purchase data as a purchase and that's because it's no longer exactly a subscription purchase because we are omitting that user ID and we're going to keep this line but we're going to wrap it in an else statement here because we would only use this line if we don't have a user ID and this is now too long so we can move it down to another line as well but this is good so just to reiterate what this is doing here by default now our purchase data does not include a user ID so then when we come down here we have all this default stuff set we're going to check if we do have a user ID if we do we're going to add it into that purchase data when we're calling the update but if not we're just going to call the update without the user ID we're basically going to do the same thing for our handle non-subscription now we're going to allow null and then we're going to omit the user ID remove the user ID here we can copy this whole block of the check here and where we're making this Firebase call which is being called the same there and our purchase data is the same there we are going to add in that user ID again so we can just remove that line because this part will be the same so because of the way we call this when we do not have a user ID we're still using Create or update purchase and if we look at our Firebase calls Creator update purchase is going to use set instead of update and the problem with that is set is going to completely overwrite the whole document so if no user ID is passed in or if it's passed in as null it's going to be removed whereas if we used update only the parameters that are passed in will get changed or updated and any that aren't passed in like the user ID in this case will just exist and stay the same so all we need to do is use update purchase instead of create or update purchase in our Google Play purchase Handler when the ID does not exist so instead of using Creator update purchase here we're just going to use update purchase and we need to update this in both sections and that should be good and after making that change make sure to redeploy the Firebase functions and now we can go ahead and fully test this so on the physical device here we can go ahead and buy the unlimited monthly and then we'll click subscribe once we purchase that a new purchase should show up here which you can see just showed up so our app is now currently on the unlimited subscription here and we can just go ahead and close out of this now so a few things to notice here one is the purchase date is at 451 and the expiry date is at 458 so this is a 5 minute subscription with this test account but there is about a 2-minute buffer on the expiry date here what we're going to be looking for is actually when 456 hits this should update and if you look at the email that it sends you it will tell you that it is going to automatically Renew at 456 so we can wait now for 456 to happen all right and it's now 456 so you can see this was updated the expiry date was updated and the subscription is still active which is what we would expect so all of this is is working as we want and the user is still marked as having that unlimited now if we were to go ahead and cancel our subscription which you could do in a few ways one you could wait for all of these renewals to happen I believe there will be 10 and then the subscription will expire or you can just expire it yourself and you can do that by just canceling it so if you go to manage and then cancel subscription we can hit no thanks to pausing it and then we can decline to answer and hit continue and then go ahead and cancel the subscription so the subscription is now cancelled we now have until 5:01 p.m. if you remember before this was probably at about :03 basically the sub the expired date is now completely accurate before it had that two-minute buffer but now that is gone but either way that doesn't really make a huge difference we do have our subscription that should expire now at 501 so at 501 we should get another notification from Google Play and that notification will be actually expiring this so this status should turn to expired and then our user which is this skq user down here if we find that user here the unlimited should go to false which means that in our app the unlimited will go to false all right and now it is pass 501 so you can see our status was marked as expired here and if we check on our user unlimited is now marked as false so this is exactly what we want to happen and in the app right now the user is back on the non unlimited version so that's exactly what we want it is very important though that you make this change here because if you keep the Creator update purchase when you don't have a user ID it is going to clear out that user's ID from this document here that is going to cover updating our server events with Google play in the next video we're going to talk about just expiring subscriptions as kind of a fallback when the expiry date has changed but the status is still active even though we have our server events set up and working correctly for both IOS and Android we want to set up a kind of a fallback expiring of subscriptions and all this is going to be doing is looking at the status here and then comparing it to the expiry date and what we'll be interested in is when the status is active but the expiry date is in the past you could imagine right now if this was marked as active then it's active but but currently this expiry date has passed because it is past this time so the goal of this next and final Firebase backend function that we're going to create is to go and find these type of purchases expire them and then also update the user to expire that user as well so we're going to export this one and it will be a constant again and we're going to call this expire subscriptions and this function is going to be similar to the Google Play server event one in that it will be a pub sub but it will be a scheduled function so we're going to schedule this to run on an interval so we can Define this with functions pubsub and then schedule for the schedule here we can actually use a cron tab like value so if you go to KRON tab. Guru and I will link this down below you can see if you just put five stars it will run this every minute so for now we can just run this every minute for testing but after testing I'm going to use this syntax which will run it every hour at the 17 minute Mark so the 17 is pretty arbitrary you can literally choose any number so we can run it every 10th minute past the hour so this run like 5 10 6 10 710 Etc so that's fine we can use that and I'll just comment this here to update later we will need to set the time zone here so that we are able to run it exactly when we think we're running it so I'll set it to the Eastern New York time zone and then we're going to call that on run and then when this runs we want to call a new Firebase function which we'll Define in our Firebase calls and we're going to call this expire subscriptions so we have not defined this yet so we can copy this and then open up our Firebase calls and create a new asynchronous function and we'll just call it that expire subscriptions and this will just return nothing but it will be again a promise void so just like the ones above we can copy that over and now we can fill out this function so we're going to find the documents where the expiry date is expired and then also where the status is active so if this status was expired there's nothing we would need to do here so we wouldn't be interested in it for this call so let's go ahead and write the query for those documents and we'll just set it to a new variable called documents and we'll have to await this call and we're going to be making that call to fir store and we're going to look at the collection of purchases and then we're going to add a couple wear Clauses on it so we're going to be looking where the expiry date is less than or equal to the current time and we can use Tim stamp. now for the current time and we will have to import timestamp from firestore timestamp but as I said we don't just want to find the ones that are expired we want to find the ones that are expired and that have a status that's equal to active and then we're just going to Simply get these documents it's possible that we won't find any documents with this because there might not be any that are actually expired and still marked active so let's check that we have some that we need to actually Mark expired so if the document size is not existent then that means we don't have any documents so we can just return but if we get down here then that means we do have documents and we can update all these documents in a batch right so we can make one call to Firebase to update all of the documents and we can do this batch right because we're going to be updating all the document status to expired so since they're all going to be updating the exact same field which is that status field we can do this in one big back batch and this will save us on some Firebase calls so we're going to create a new constant here called our right batch and this is just going to equal a fir store batch and now we can go ahead and get the documents call docs on it and then for each and then we'll have access to that individual document and here we're going to be checking if the document is related to a subscription and if it is we need to update the user document as well so that the unlimited status is marked as false so we've done this logic before but we're going to be checking if it is a subscription which is essentially what we do right here this was kind of doing the opposite in checking if it wasn't a subscription so let's paste this in modify so we're going to be looking at our two subscription types and if that includes the this is actually just going to be the document data instead of the purchase document and we will be calling product ID on that so if this is a subscription then we can get the user reference by creating a new variable here for the user reference and this is going to be a firestore collection of users and then get the document for the user ID and now that we have this user reference we can just update the unlimited parameter to false so this Loop here will go and find each user document related to the purchase document and update that user to have unlimited false but we still haven't done our batch right to actually update the purchases to be expired so for that we will be using the right batch and we're going to be updating that and this will update the document which we can find by the document reference and then we need to pass in what we want to update which will be the status and we want to set that to expired so this is essentially going to keep updating this right batch but it's not actually going to write it or save it so outside of this Loop in the document we can go ahead and actually write that so await the call to write batch and then we're going to just simply call commit on it and this is going to save all of that so that will be it if we go back and look at this everything should be okay we seem to have this tapped in by one too many but this looks good our function will run once a minute so if we deploy this what's going to happen is this one right here we changed so that it is expired but it is active so once we deploy this and it will run this should just be marked as non-active as well as the related user so if I were to go to this related user and also mark them as having a True Value here then essentially at this point it's as if this subscription is expired but it wasn't marked expired and neither was the user so to test all of this we'll just deploy this function let it run once and then we'll see if that does expire all right and now our function has deployed so if we go back to Firebase console and wait about a minute it should run that function and then this should be up updated to expired if we look over at our function in the functions here we should see we have a fourth one which is going to trigger every minute and we can just check the logs for this and it looks like we do have an issue here the problem is with the query that we built here in the Firebase calls we have two wear Clauses and whenever you have that you need to create an index so luckily in the logs it does kind of point us to where we need to go so if we copy this link right here and paste it in a new tab it will pop up immediately with the index that we need to create and we can just go ahead and hit create index there you can see the status of the index right here and it will take a little bit of time but once this is done building we can check it again and it should all be working all right so our index is now enabled and if we go back into the logs here and run the live logs we can see that this is no longer airing and we do get that status of okay so if we go back and check into our example that we were looking at this is now marked expired and the last thing we need to check is that this user was also removed from having that unlimited which yes they were so this is a good fallback just in case anything is missed in the updating of the expiry date and status when we're getting the server events from Apple or Google and really it's just kind of a a last final cover case that is going to expire anything that is actually expired but still marked active and it's pretty quick to set this one up so definitely worth doing as well all right that is going to be it for this section we now have our backend fully configured and we have our four Firebase functions which can be seen under the functions here again I would change this frequency because I don't think it's necessary to run it every minute I think once every hour is enough and that even might be a lot you could potentially get away with running it once a day depending on how tightly you want to be expiring people's subscriptions but do note that the expired subscriptions is not really the main way that subscriptions are going to get expired it's these handling of the server events that are going to be mainly used to expire subscriptions yeah setting up this back end is definitely not the easiest part but now that we have it complete we can basically move on to deploying our app there are just a few small things we still need to do but essentially we are just about done so before we're able to actually deploy anything we do need to add a privacy policy and terms and conditions to our app and because we have inapp purchases we do need to have that within the app itself so you can see where we have our restore purchases button we're going to add two more links one for the terms and conditions and one for the privacy policy in my experience if you don't have links directly in the app Apple will deny these when you try and submit it to the app store Google Play may be a little bit more flexible but I think both of them just generally require it and should have it and both of them also have areas where you can upload it within the consoles themselves and I'll show you how to do both of those firstly though we need to generate a privacy policy and terms of service and I always like to use iubenda for this it makes it very easy to generate these I already have one for the decider app so I kind of just walk through this a little bit but it's pretty simple to set these up you essentially just fill in your information and then choose the services that you have in your app so you can see there's basically this Builder here for the privacy policy and you can add a service and I just went through and already added the services that I'm using in this app I do have admob I have the Apple app store Google Play Store all this stuff is included in the app and you can just add any new service by clicking add service and basically searching they have a huge list of almost everything you could imagine you can also create custom ones if they are missing anything but this makes it very easy and then if we go back to the dashboard and we view this I do have decider on the ultra plan and that allows you to do the terms and conditions as well as the privacy policy the terms and conditions are going to be required when you have the Ina purchases build this you would edit it in a similar way but there's different information here that you're going to be filling in but it does walk you through all of this and I think it's pretty simple so if you do have any questions about this feel free to comment down below this video generally I don't look too deeply into this stuff I just let aenda kind of handle and manage these for me which is nice there is a cost to this and it is not free the ultra plan is definitely a bit more expensive than just the privacy policy so definitely something to consider if you want to use this or you could look at other options but for me this is just the easiest way to do it and something I could trust to know that this is all just handled and taken care of it's a little bit over $100 to get the ultra and I do have a link below where you can get 10% off so if you are considering using this that could save you a little bit of money there but whether or not you actually use iubenda you need to get a privacy policy link so you can see here I have two links one for the privacy policy and one for the terms and conditions so if you go to this link is just a public link that has the privacy policy so these links are what we're going to actually be linking to in the app so we're going to do this in the store View and we're going to want these links to be clickable and opened in a new window so kind of similar to how we have our build restore buttons we're going to create a new widget and build the terms and conditions buttons so actually let's just go ahead and copy this entire thing and rename it because it is going to be very similar so we're going to call this build terms buttons and now we can go up to where our build restore buttons are and just paste this right below it so that is as we would expect but let's modify this now to actually be the two buttons that we want so firstly we have this text button and we are going to change the title here to be the privacy policy the style here can stay the same and in the onpress here we want to just put that link so we have that link here for our privacy policy and I'll copy that and if we just paste the link as a string it's actually not going to work so if you were to click on the privacy policy nothing happens because this is actually not going to do anything for the link so en able to launch this URL we are going to add a new package and it is called URL launcher so we'll add this package to our pubspy yaml and we will will run Pub get this will give us the URL launcher package and what this is going to allow us to do is actually launch URLs from our app so let's create a function that we can pass this URL into and it will launch that URL for us so this is going to be a pretty simple function and we'll call it launch URL and it will take a URL parameter if your app is going to have multiple URLs outside of just this one page I would recommend moving this to its own widget but my app is pretty simple so I'm just going to to stick it in this store view here this will be an asynchronous function and then we will await a call to launch the URL but we want to make sure that the URL can launch first and that is part of the package it has a can launch and you can pass it in a URL as a string and if it is able to launch the URL then we simply want it to actually launch that URL this is going to be another await call to launch and we will use that URL parameter again if it wasn't able to Launch so it would be on the other side of this on line if statement then we can just throw an error and say that we could not launch the URL so this is good we can now wrap this call in the launch URL and if we rerun this and then go into the store and click on the privacy policy link you'll notice it still does not work if we look in the runner logs here there's a missing plug-in exception and this is because the app does not have the permissions to launch a URL currently so we do need to add that in so for iOS we can update our info. pist which is going to be in the runner file over here and we need to add in what's in the documentation here for the iOS and what this is going to do is allow our app to open https and HTTP links so we will add this block here right at the bottom of our info. pist but make sure it's inside that final dict and we can save this and then we're going to need to run a flutter clean and now we can rebuild the app and and once that reloads if we go into the store and click on the privacy policy link you'll see it does open that link up so it's exactly what we want there let's add this last link and then we'll test this on Android so we have our privacy policy we also want our terms and conditions and this link is going to be for me the link right there so I'll paste that in here as well and then go ahead and save this and now if I click the terms and conditions you can see there are the terms and conditions and the privacy policy both linked there there is one final last step that we need to configure on Android so let's go ahead and open an Android simulator now and while that's running we can update what is in the documentation here so we need to just add this first line in the queries here for the view of an https scheme and this is going to be in our Android manifest.xml so if we go up here into the Android folder and then apps SRC under profile we want the profile Android manifest and below this we're going to need to create a new block for queries and then we can paste what we have right here and now we can run this on our emulator and go and test those links in the store View and if we click the privacy policy since this is basically a new emulator this will pop up we can accept that and click no thanks and then you can see we are on that privacy policy which is good and the terms and conditions also works in this video we'll update both the App Store connect and the Google Play Store with our privacy policy so first the App Store connect if we go into our project and then choose app privacy you'll see we have no privacy URL so if we edit the privacy policy here we can paste in our privacy policy that we have created and the user privacy Choice URL I am going to leave blank and I won't actually be supporting that at all in this app so I'll just hit save here so now we have the privacy policy but we also will have to go through this for Apple so we can click get started here and this is kind of related to the privacy policy but it's more about just what data you're going to be collecting in your app so you can go through this and fill it out and generally any app you are are going to be collecting data so you will hit next there and it's going to kind of ask you what kind of data you're collecting so what information from the user will you be collecting and really none of the users data is being collected in my app but I do have a little bit of usage data that is probably going to be collected like crash data and some performance data I'll also be collecting some usage data so I'll select those purchases will also be tracked so we will collect those we also will be using a user ID not device ID though but generally this is going to be specific to your app so definitely go through and and make sure you are setting up what you need to for your app then within here you're basically going to have to say how each of the types of data that you're collecting are being used so I'll give you the example with the user ID here the user ID is just for app functionality right now you can see the example is authenticating the user that's exactly what we're using this for so we'll click next there and then it's going to ask us if these IDs are actually connected to the user's identity and in my app that is going to be a no because it's just an anonymous user ID so there's actually no way we know who that user is but again this is going to depend on your specific app how you're going to answer these questions and there is no actual tracking so we can hit next here this is just kind of more information so you definitely should read through all of that that and lastly it's going to ask if you use the user ID for tracking purposes and this is really about ad tracking So currently I do not use the user ID for that so I'm just going to go ahead and hit save overall the data is not linked to you that's what this will become you will need to complete this for all the other data types I'm not going to walk through that but that's generally it and as long as you have the privacy policy here you should be good this is going to be a required step before you can publish your app on the App Store so you will need to make sure you complete this in once you do complete the app privacy in app store connect and for the sake of this example I have it not collecting any data right now you will want to mark this as published so you will publish this to your app and you can see that it was published so definitely make sure you click that publish button once you have filled out this page completely now we can look at the Google Play console and update our privacy policy here so when we have our app selected we're going to scroll all the way down to the bottom on the left and go to to the app content and you can see we still have a few things in the to-do one of them being that privacy policy so we can go ahead and click Start there this one's pretty simple we just need to copy that privacy policy link and paste it in there and that is it for the privacy policy the data safety here we can also do real quickly and this is kind of a similar thing is going to be talking about how our app collects data so definitely read through all of this and it's just going to be really asking you questions of what data you're collecting and how you're collecting it and what you're doing with it so the basically the same thing that we had to do over here with Apple so I'm not really going to walk through this step by step because it is going to be so different for everyone's individual app but this is also a required piece before you can actually get this properly deployed on the Google Play Store but once you go through both of those you'll be pretty much set up with your privacy policy and your terms and conditions and in terms of that privacy user data information your app will be ready to launch so the next video we'll talk about about actually submitting your app to the App Stores so we have everything ready now to deploy the apps to the App Store and the Play Store the app on the Play Store is pretty much ready to go all we'll need to do is just create a new release with all of the code changes that we've made and then we can just move that to production there is really no extra steps that we will need to do that we haven't already done to get the inapp purchases working so there's not really much to go over there you can just upload another app build and then actually deploy it when ready the App Store however there will be one other small step to do when submitting this with the Ina purchases so first you'll have to fill out all this information and then upload a build so I did update the screenshots here and the description we can go to the manage and the inapp purchases and this is where we have to add one last part each of these inapp purchases there is a section if you click into it that has the review information and really what they're looking for is a screenshot of where the Ina purchase can be purchased and then some information about what the Ina purchase does and why you're offering this to the user so this is basically what I will put for all of them so this is the five decisions so this in purchase adds five decisions to the user's account it can be purchased from the first card in this list captured in the image and then for that image I'm just going to show the store View and you can just use your Simulator for this or you can run it on your physical device but I'm going to capture a screenshot of that and then I'm going to upload that screenshot right on into this review section here I will be using that same screenshot for all of my Ina purchases since they're all part of this store but I will modify the text here to have the slight difference in each of the different purchases so 50 and 500 decisions and then for the premium I'll describe that the premium is more about getting an adree experience and that the unlimited monthly and yearly are both about having an unlimited amount of decisions with no ads so depending on what in purchases you're actually going to have in your store you'll just need to fill out this information and potentially the screenshots are not all exactly the same you might offer these in different areas of your app and that's fine too really I think what they're interested in is just being able to see where these are offered and what they actually are so if you save this you'll notice that the status changed from waiting for metadata to ready to submit so that's a good sign we will need to do this for all of the Ina purchases we have and really you just want to get these all in that ready to submit status because when you do submit your app all of your inapp purchases that are linked to your app will also be submit for review so this is kind of that final step there so I'll just go ahead and fill all of these out so you just want to make sure that all of the inapp purchases are in that ready to submit stage and then the last thing you need to do is go back to the prepare for submission tab up here and you are going to need to upload a build for your app which you can do through xcode so I can show you that real quick this is kind of just basically how you would typically deploy an app so if you're in xcode and you have your app open and you choose the any iOS device and then go product archive this will essentially create a build of it that you can upload to xcode if you've never deployed an app before this is how you would do that and I'll link to some more Guides of how to just generally deploy apps once the archive is done running this page will pop up where you'll see the app in its compiled version and then you can just distribute the app this is going to actually upload it to the App Store connect if you choose the upload option there and this process can definitely take a little bit of time it will load upload to App Store connect and then it does go through some processing on that side as well you'll get some emails that alert you when this is is available this is basically how you would deploy your app or how you could get it to work with test flight but ultimately you will just upload the build right there so once that build finally uploads you'll be able to select it from the options here but most importantly what you will need to do specifically for the Ina purchases is select the Ina purchases as well and you can see these are now available because they were all marked as pending approval so we are going to select all six of them and then click done so all the purchases will be here and for the app review information you'll need to fill this out as well but once you upload that build and select it that's really all you need to do and then you can just submit this for review and then it will go off to Apple and be reviewed in my experience it never takes more than 24 hours to get a full app reviewed and they'll either come back with some feedback that says certain parts of your app need to be updated or maybe you're missing some metadata but overall if you follow all these steps and your app is a legitimate app it should have no issues being reviewed once you do get your app fully launched you can head on over to the Discord server and post a link to it in the self-promo channel and there we can all check out each other's apps give some reviews and begin to build our user bases out there is one final change we need to make to our backend code before actually deploying our app and this is pretty simple but I do want to make sure you're aware of this when you go into the App Store purchase Handler if you remember when we set this up we set it up with the sandbox and the production when you're finished testing you can just have it set to the production environment but do note that if you do start testing again the sandbox test purchases will not work anymore with our backend validator so do note that but you would want to change this to just production for your actual production deployed version and you can save that then you'll just want to make sure to deploy this with with a Firebase deploy once that deploy is complete you'll see the handle App Store server event URL is right here it'll also be the same URL that you can get just right within here but we need to update that URL in our app store connect so if we go to the app information in our app and scroll down a little bit you can see we already set it for the sandbox URL and we can also Now set that for the production URL and hit save depending on how you're going to be developing you might want to have two separate URLs one for sandbox and one for production I'm going to go ahead and just clear this one for now so we don't even have a Sandbox one set up and it's just production and once you make sure that's saved there is really nothing further to do for the backend configuration once your app has been deployed if you did include any ads in it there is one last step that you need to do in Google admob and what you'll do is go into your app in Google admob and go to the app settings and then within here you're going to want to add the App Store so click add there and then you're going to search for your app and this is actually going to search the App Store so you can see my app is right here this has already been added but one thing to note is that it does take quite a bit of time for this to show up in Google admob I believe it took about a week for me so you will just need to continue coming back and checking until you find it so that you can add and Link it there without this being linked your app is not going to get ads served in it so this will be required after you deploy the same concept will be true for the Google Play version of the app and you'll do the same thing in app settings you can just add the app store to there as well great so we covered the various ways that you can monetize a flutter app if you've gone through the complete course you should have a a complete app on both IOS and Android that is ready to be tested by actual users and to deploy that there's only a few last steps you just need to add some screenshots descriptions and then the app is pretty much ready to go if you have any questions at all definitely comment on any of the videos comment down below or email me directly also you can connect with me on Twitter at Dave fisy or join the Discord server where you can talk with the entire Community I hope you've enjoyed the course and I hope you've learned a lot and it made monetizing your app a lot simpler if you've gone through this whole course and monetized your app and deployed it share the link with me on Twitter and I'll definitely retweet and I'll try and help get as many people to download that app for you as I can but yeah I would love any feedback you have on this course I hope it was useful to you but let me know if there's anything that's missing and I will do my best to add that all right CIA for [Music] now [Music] oh
Info
Channel: 1ManStartup
Views: 11,381
Rating: undefined out of 5
Keywords:
Id: w7oqVDGMMJU
Channel Id: undefined
Length: 552min 22sec (33142 seconds)
Published: Wed Nov 08 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.