‫And now to finish this project,
‫let's finish this Stripe integration with webhooks.
‫Let's start to remember how our Stripe integration
‫actually works right now.
‫We have this checkout-session endpoint,
‫which gets called from our front-end.
‫This will then call the getCheckoutSession function,
‫so basically this one here,
‫which will create a checkout session on the server
‫using all of this information here,
‫and then send it back to the client.
‫Then after the processing of the payment
‫is successfully done by Stripe,
‫then redirect the user to this success URL,
‫so this one that we created.
‫Remember that onto this URL, we added the tour ID,
‫the user ID, and also the price.
‫We did that so that once this URL here is then called,
‫our application would create a new booking document
‫in our database.
‫How did that work?
‫In the my-tours route, we have a middleware for that.
‫Remember, here in the viewRoutes, in my-tours,
‫we have this createBookingCheckout.
‫This function here, which basically from the query
‫takes the tour, user, and price,
‫and creates a entry in the database using that data.
‫Basically we put this data on the URL
‫whenever Stripe successfully processes a payment.
‫And then this middleware function that we have here
‫picks up the data and creates a new booking in our system
‫using that data.
‫And then after that, we basically redirect here
‫onto the original URL without the query string.
‫Now the problem with this was that it's not really secure.
‫So, everyone who knows this URL structure,
‫so this one up here, which tour, user, and price
‫in the query string, can basically create a booking
‫in our system without actually paying.
‫So, all that we'd have to do is to open up this URL
‫with some data in there
‫and then from there automatically create a booking
‫without going through the Stripe process.
‫Remember how back then I said that we would fix this
‫using something called webhooks.
‫So, we do that now.
‫Because for that,
‫we actually need our website to be deployed.
‫Now at this point, that's actually the case.
‫And so, now we can implement these webhooks.
‫For that, let's go to our Stripe dashboard.
‫And I actually already have that opened here.
‫And then go here to developers, then choose webhooks,
‫and here, add a new endpoint.
‫Now what is this endpoint here and this webhook?
‫Basically we're gonna specify a URL here
‫to which Stripe will automatically send a POST request to
‫whenever a checkout session has successfully completed,
‫so basically whenever a payment was successful.
‫With that POST request, Stripe will then send back
‫the original session data that we created in the first step
‫when we created that checkout session.
‫That's the reason why we actually needed our website
‫to be deployed here because now we need to specify
‫that real-life URL here.
‫Let's grab that from here,
‫and then add our route here basically.
‫I'm going to call this one webhook-checkout.
‫It's not in the API, and it's not inside the bookings.
‫You will see in a moment why that is.
‫Again, when a payment was successful,
‫Stripe will then automatically post
‫the original session data to this URL.
‫Now we also need to select the event.
‫And you see there are tons of events that we could use here.
‫The one that we're using is the checkout_session_completed.
‫Add that.
‫Now you need to put in your password here again.
‫And then there we go.
‫This webhook then also has a secret here.
‫This one, we will then also need in a second
‫when we actually create our route for this URL here.
‫Actually that's exactly what we're gonna do next.
‫Basically in our system of course,
‫we now need a route for this here
‫so that when Stripe then posts the data to our application,
‫we can then actually do something with it.
‫Let's go back here and open up our application.
‫We will actually add this route right here.
‫Again, I will explain you why in a second.
‫So, app.post
‫and standard route and then of course we need
‫a handler function for that.
‫Let's quickly create it here in our bookingController.
‫Let me call that export.webhookCheckout.
‫Now I will have to import this controller into app.js.
‫Let's do that right here after the bookingRouter actually,
‫so this one and this one,
‫controller and here also controller.
‫All right.
‫Now down here, that's bookingController.webhookCheckout.
‫Now why do we actually define this webhook-checkout
‫right here in app.js instead of doing it for example
‫in the bookingRouter.
‫The reason for that is that in this handler function,
‫when we receive the body from Stripe,
‫the Stripe function that we're then gonna use
‫to actually read the body needs this body in a raw form,
‫so basically as a string and not as JSON.
‫Again, in this route here, we need the body
‫coming with the request to be not in JSON,
‫otherwise this is not going to be working at all.
‫Now the thing is, that as soon as a request
‫hits this middleware here, the body will be parsed
‫and converted to JSON.
‫It will then be put on request.body as a simple JSON object.
‫Again with that, this route handler here
‫would then not work.
‫That's the whole reason why we need to put this route here
‫before we call the body-parser.
‫Now we still need to actually parse the body
‫but in a so-called raw format.
‫By the time I was recording this video,
‫we could not do it with Express out of the box.
‫And so, in this video, we download the body-parser from npm
‫and use it as I show it in the video.
‫However, like five days after I recorded this video,
‫Express added the raw parser to Express as well.
‫Now we can use express.raw instead of having to install
‫the body-parser or middleware from npm.
‫Again, in this video, I will now install the body-parser,
‫but you don't really need to.
‫You can just use express.raw instead.
‫Npm install
‫body-parser.
‫This probably all sounds a little bit focusing,
‫and I totally understand that,
‫but that's just how the Stripe documentation works
‫and forces us to do it, really.
‫Let's go back here to our route.
‫In this route, we need the body to be in a raw format.
‫We can add that as a middleware here between the route
‫and the final handler.
‫Here we say bodyParser.raw,
‫and we also need to specific here the type
‫just very quick as application/json.
‫We now added this body parsing as a raw body
‫here in this middleware stack.
‫All this will really start to come together
‫once we start implementing this function.
‫Actually let's do that now,
‫so right here.
‫But before we actually do that,
‫let's get rid of all the code that we wrote
‫in order to make it work right now.
‫So, basically this middleware function,
‫we don't need it anymore.
‫Also here in the viewRoutes,
‫we don't need it here anymore either.
‫And then finally in the bookingController,
‫let's also set our URL back to normal.
‫I will just leave all of this here
‫so that you can keep it as a reference.
‫But now the success URL should actually just be this.
‫Basically after a successful booking,
‫we still want to come back to my-tours
‫but without all this query parameters
‫because now it's no longer this function here,
‫which will take care of creating the booking
‫but instead it is this function here,
‫which is of course the one that gets called
‫once Stripe calls our webhook.
‫Let's now implement this.
‫The first thing that we need to do
‫is to rid this Stripe signature out of our headers,
‫so signature and then request.headers
‫and then from there stripe-signature.
‫Basically when Stripe calls our webhook,
‫it will add a header to that request
‫containing a special signature for our webhook.
‫If you're thinking that you're just blindly following
‫what I'm doing here, well, (laughs)
‫that's actually exactly what I did as well
‫from the Stripe documentations.
‫Again, this is really just how Stripe works,
‫and there's nothing we can do against that.
‫Anyway, next up, let's create a Stripe event,
‫so const event equals stripe.
‫For that of course, we need to the Stripe library installed,
‫which we have up here.
‫So, stripe.webhooks.contructEvent.
‫Now here is where finally that body
‫comes into play, so request.body.
‫And remember that this body here
‫needs to be in the raw form,
‫so basically available as a string.
‫Once more, that is why we put that route
‫before all our other routes
‫and especially before the body parser could do its job
‫of converting our body into a JSON object.
‫Then besides that body, for the event, we need a signature,
‫so basically the signature that was sent
‫along with the header, and then finally our webhook secret.
‫Let's get that from here, copy it.
‫Since it's a secret, we should, as always, add it here
‫to our config file,
‫so STRIPE_WEBHOOK_SECRET.
‫And then later of course, don't forget to also add this
‫to our Heroku configuration.
‫Let's now use that.
‫Add process.env.
‫I should have just copied that
‫right here.
‫So, you see, all of this is really to make the process
‫super, super secure.
‫We need all of this data like the signature
‫and also the secret in order to basically validate
‫the data that comes in the body
‫so that no one can actually manipulate that.
‫Now during the creation of this event,
‫there might be some errors,
‫for example if the signature is wrong
‫or if the secret is wrong.
‫And so, let's wrap this into a try-catch block.
‫Okay.
‫Of course, we now need the catch.
‫In case there is an error,
‫we want to send back an error to Stripe,
‫so return res.status 400
‫and then just use send webhook error
‫and then let's just add the error.message.
‫So, it is Stripe who will receive this response here
‫because again it is Stripe who will actually call the URL,
‫so our webhook, which will then call this function.
‫Now we need to of course also declare this event here
‫outside of the try-catch block
‫because otherwise we will not be able to use it down there.
‫So, let event and then reassign down here
‫because remember that the ES6 const
‫and let are block-scoped.
‫And so, this variable would not be available outside
‫of this block.
‫Now let's actually use that event.
‫First off, we need to test if this really is the event
‫that we want.
‫So, we can do event.type
‫is equal to checkout.session.complete.
‫Remember that in our Stripe dashboard,
‫that's exactly the type that we defined here.
‫So, that's the event type.
‫Now we're checking if that is really the event
‫that we are receiving here just to be 100% sure.
‫If it is, we then want to actually use the event
‫to create our booking in our database.
‫Actually let's do that in a separate function
‫and not inside here of all of this mess.
‫For that, I will create a function.
‫Actually let me give it
‫this exact same name, so createBookingCheckout.
‫It was actually a nice name,
‫but now it cannot be a middleware
‫but instead just a regular function.
‫This function will accept the session data.
‫And remember that the session data is exactly this session
‫that we created here in the first place.
‫If this is the correct event,
‫then let's actually call that function,
‫so createBookingCheckout with the session,
‫which is at event.data.object.
‫And then finally,
‫let's just send back some response to Stripe.
‫So, status 200
‫and then let's say some json
‫receive set to true.
‫Makes sense?
‫Once more, all of this code here will run
‫whenever a payment was successful.
‫Stripe will then call our webhook, which is the URL,
‫which is going to call this function.
‫And so, this function receives a body from the request,
‫and then together with the signature and/or webhook secret,
‫creates an event, which will contain the session.
‫And then using that session data,
‫we can create our new booking in the database.
‫And so, that will actually be pretty similar
‫to what we had here before.
‫So, we will need this line of code here again.
‫So, this will also be an async function.
‫And so, this is exactly the same.
‫Now what we need here of course
‫is to get access to the tour, user, and price.
‫But that data once more is stored in that session.
‫So, let's start with the tour.
‫And remember how up here
‫when we first created this handler function,
‫I specified this client_reference_id field
‫and added the tourId to that.
‫Remember that?
‫I did that because, at the time,
‫I already knew that we would need this tour ID a bit later.
‫Now it's that time where we actually need the tour ID
‫in order to be able to create that new booking.
‫And so, now the tour ID that we need
‫is at session dot client's reference ID.
‫So, let's copy this and say
‫session.
‫And of course, here we need to say tour.
‫So, that's the tour ID.
‫Next up, we need the user ID.
‫Now the information that we have in our session
‫about the user is the user's email.
‫And so, now what we need to do
‫is to basically get the user's ID.
‫For that, we will query by the email.
‫That's no problem because the email is also unique.
‫Based on that, we can then find the unique ID.
‫So, const user is await.
‫And I think we already have the user here.
‫No?
‫No, I actually don't.
‫So, let's just do that here.
‫And user here as well.
‫Okay.
‫So, User.findOne and then query via email,
‫which is in session dot,
‫and I believe that's client's email or something.
‫It's customer_email.
‫Okay.
‫But that will then return the entire document,
‫but we want actually the ID.
‫So, let's wrap all of this here in parenthesis
‫and then call the ID on there or actually read the ID.
‫So, that's it.
‫And finally, the price.
‫Where is the price stored?
‫Well, it's here in line_items.
‫That's an array, so at element zero,
‫and then the amount divided by 100.
‫So, we multiplied it here by 100 to get cents,
‫but now of course we want it back in dollars.
‫So, we need to basically divide that back.
‫And so, session.line_items
‫and then the first element dot amount if I'm right.
‫Yeah.
‫So, amount divided by 100.
‫That should actually be it.
‫Let's now commit our changes to the repo
‫and push it to Stripe.
‫So, git add all, of course,
‫and then git commit message
‫Improved stripe implementation,
‫and then git push heroku master.
‫Once more, this will take some time.
‫I'll see you when that's done.
‫All right.
‫Now don't forget to set that new environment variable.
‫So, that's heroku config colon set,
‫and then simply copy it from here.
‫Okay.
‫That then restarts the application.
‫And that's it.
‫So, let's now actually go ahead and test it.
‫All right.
‫We are still here in our application.
‫Let's see which tours Laura has already booked.
‫She has the Forest Hiker.
‫That booking was still done using the old method.
‫But that old method now no longer works.
‫Now if we do another booking and it works,
‫well, then that's going to mean
‫that of course our new implementation works.
‫Let's test that here.
‫As always, 4242.
‫Now let's wait for it.
‫Well, that apparently didn't go so well
‫because otherwise our second new tour
‫should already be here in our bookings.
‫Let's see here in our dashboard.
‫If we now reload this,
‫then we actually see that there was a successful event.
‫So, that's the event that we just created
‫and which sent this body here
‫and then received this response.
‫So, this receive set to true
‫is exactly what we did here in our code,
‫so this here.
‫So, that's the response that we sent
‫and the body that we got in was all of this data.
‫And so, we can see here the session with the price,
‫with the email, with the tour.
‫And so, I'm not sure why it didn't work.
‫So, let's just quickly reload this here.
‫So, actually our Stripe implementation should be correct,
‫but, for some reason, our new booking was not created.
‫Let's check that also here in Compass.
‫And indeed, it's not there.
‫So, let's go back to our code here.
‫Oh and one error that I see right away is here.
‫So, it should be completed like this.
‫So, that's a stupid mistake.
‫Let's just see if there might be another error
‫up here in createBookingCheckout.
‫Here we have
‫line_items.
‫Let's see if that's correct.
‫And yeah, it seems to be.
‫We can also confirm that here again in our Stripe.
‫Actually here it's called display_items.
‫That's weird.
‫Just to make sure, let's also call it display_items
‫here in our code right here.
‫Now another thing that I noticed
‫now as I took another look here
‫is that we still have this image hardcoded
‫to this other natours.dev.
‫Now let's actually fix that because at this point of course,
‫our website is already live and deployed.
‫And so, we can basically replace that
‫with the same as we have here.
‫So, we use this part here many times.
‫And so, it's time to use that again here.
‫Yeah.
‫Let's try to redeploy this.
‫So, git add all again.
‫And let's just call this here
‫Improved stripe implementation two.
‫And then push it again to Heroku.
‫Okay.
‫Let's try that one more time.
‫Let's go back here.
‫Now let's try to book again to Park Camper.
‫All right.
‫You ought to see the image popping up here on the left side.
‫That means that our new image integration
‫also worked just fine.
‫Now it's processing.
‫Ah now it is here.
‫Great.
‫That's beautiful.
‫Now we really have a secure
‫and way more professional Stripe implementation
‫in our application.
‫That's great.
‫Of course, if you reload here,
‫then you should see this new event here,
‫so this new call to our webhook,
‫which of course again was successful.
‫That's just great.
‫Now there's just one final thing that I want to do,
‫which is to basically give the user some feedback
‫in form of one of these green messages
‫that we use also for example in the login.
‫Right now our application doesn't really give
‫any kind of feedback when a new tour was booked.
‫Now I want to change that.
‫However, doing this is not really straightforward
‫because remember that these messages
‫are actually displayed by JavaScript.
‫So, in the other cases, we did an HTTP call to our API.
‫And then when that was done, we used JavaScript
‫to display some kind of message.
‫But now we do not do it this way.
‫And so, the message should already be somewhere in the HTML
‫as soon as the page loads
‫so that then our JavaScript can pick that message up
‫from the HTML and display it nicely up there
‫in one of these banners.
‫And so, the way I'm going to put these alerts
‫in the HTML is once more by using a data property.
‫Let's start by implementing this feature right there
‫in our main template.
‫That's here in views, base.
‫I will actually add that alert message
‫right onto the body element.
‫Here we will have a data alert property,
‫which should actually only be set
‫if the alert variable is available here.
‫So, let's use ES6, so a template string,
‫and say if there is an alert,
‫then use alert here, and else, an empty string.
‫And so, this alert here will be the alert message
‫that JavaScript will then pick up and display on the page.
‫Now how does this alert message then actually end up
‫as an alert variable here in our template?
‫Well, I came up with a solution that is reusable
‫so that we can use all over our application.
‫That is that on the query string,
‫we will add some alert keyword
‫and then we will have a middleware,
‫which will take that keyword from the URL
‫and, according to the keyword that we put there,
‫will then put a whole alert message on response.locals.
‫And so, remember that everything that's on response.locals
‫is then available as a variable in all of our templates.
‫So, we actually used that before
‫in our authController, I believe.
‫Very quickly, let me show that to you.
‫Right here, we said response.local.user
‫and put the current user there.
‫Then automatically in all templates,
‫we have access to that user variable.
‫So, let's now implement what I just said
‫and starting with the URL.
‫What I'm gonna do here is to actually add that query string
‫here to the success URL.
‫Here, I will say alert equal booking.
‫Now I could, in all other URLS, also add some alert
‫and then with a different keyword here, of course.
‫And we will just do it here really for this booking.
‫But again I created a kind of reusable solution here.
‫Anyway, now in our routes, we need basically a middleware,
‫which will run for all the requests.
‫And it's that middleware, which will pick up the alert
‫from the query string and put a alert message
‫onto our response.locals.
‫So, router.use
‫viewsController.alerts.
‫And so, this is a middleware function,
‫which will basically run for each and every single request
‫that's coming into this router,
‫so basically for all the requests to our website.
‫Now let's actually create that middleware
‫in our viewsController.
‫So, exports.alerts
‫request, response, and next.
‫And so, the alert
‫is request.query.alert.
‫And so, let's just use this structuring here once more.
‫And then let's say if alert is equals to booking,
‫so the alert that we put right here in the query string,
‫well, then in that case,
‫let's say response.locals.alert
‫will be
‫your booking
‫was successful,
‫please check your email for a confirmation.
‫And we should also add some other phrase,
‫which is this one, if your booking doesn't,
‫select this, doesn't show up here immediately,
‫please come back later.
‫And this last part
‫is because Stripe does very specifically say
‫in their documentation that sometimes the webhook is called
‫a little bit after the success URL is called.
‫In that case, that success URL would then show
‫all of the current tours, but only after that,
‫the webhook would be called
‫and the tour would be created in our database.
‫Therefore, the new booking would not show up right away
‫on the My Bookings page.
‫But of course, everything still worked well in that case.
‫And so, I simply reload, but later we'll fix that problem.
‫Now we just need to call the next middleware.
‫And that's actually it.
‫Again, we only did this here for alert equal to booking,
‫but we could now use this all over the place
‫in our website by setting different alert keywords
‫and query strings.
‫With this, we put this message here onto res.locals.alert.
‫Again, our base template will then pick that up
‫and display it here into this data alert property.
‫And so, all that is left to do now is to go to our index.js
‫and read the alert from here and then display it.
‫And so, that should be fairly easy.
‫Here in public, let's actually do it right in the index.
‫And the first thing is that we actually need to import
‫the alerts function.
‫That's not an app.
‫It's here in index.
‫Okay.
‫And then down here, let's basically read that alert.
‫So, const alertMessage, let's say,
‫is document.querySelector,
‫then the body element, dot dataset.alert.
‫And so, only if there is an alert, of course,
‫then show the alert
‫with success
‫and the alert message.
‫And now this one small thing that I want to do
‫is to change a little bit this showAlert function here
‫because we actually have a lot of text now.
‫And the standard time that the alert is shown
‫would not be enough to actually read all the text.
‫So, you see here that after five seconds,
‫the alert is hidden.
‫Let's actually allow the user to specify
‫the amount of seconds that the alert is shown.
‫We will do that as a default of five seconds.
‫Here, we then simply do time times 1,000
‫to convert it to milliseconds.
‫Like this, all the functions will work everywhere
‫with five seconds.
‫Let's actually make it seven seconds
‫if we don't specify anything.
‫But if we want, we can then override this seven.
‫And so, I will that now here
‫and actually put it 20 seconds on the screen.
‫All right.
‫I think that should be it.
‫I hope that made sense.
‫Let's now just very quickly compile our bundle.
‫That's npm run build, then tap autocomplete.
‫That takes a little bit of time as well.
‫But now it's done.
‫Let's now deploy it one last time
‫hoping that it works actually.
‫So, git commit.
‫So, Stripe messages.
‫And one last time, git push heroku master.
‫Let's now test it by buying yet another tour here.
‫Let's get the City Wanderer this time.
‫Oh I just see that there is a message already here.
‫That's not good.
‫And you see that it disappeared after 20 seconds.
‫So, it seems like now, by default,
‫it will always put this alert class here.
‫(laughs)
‫Yeah.
‫That's because here it should be alertMessage
‫and not just alert.
‫But anyway, let's now just test
‫if the message actually is correct when we book the tour.
‫Okay.
‫Now let's wait for it.
‫Here we go.
‫Indeed, there is our message.
‫So, beautiful.
‫Also, our tour shows up here.
‫And you see that it really stays here for a lot of time.
‫So, that also works.
‫Let's just very quickly...
‫And first, we actually need to rebuild the bundle here.
‫Then we can add everything to our staging area,
‫Message alert bug fix.
‫So, these are some (laughs)
‫really professional-sounding messages here already.
‫Now one final push to Heroku.
‫Now when we load our page,
‫we should see no alert message.
‫And indeed, now everything is clean.
‫And so, I can now say that at least for now,
‫this project is really finished.
‫Once more, great job, congratulations,
‫and well done for probably being one of the few people
‫who actually are making it all the way to the end
‫of the project and really building this beautiful website
‫and also API that you can now put on your portfolio
‫and show the world.