All language subtitles for 010 Finishing Payments with Stripe Webhooks_Downloadly.ir_en

af Afrikaans
sq Albanian
am Amharic
ar Arabic
hy Armenian
az Azerbaijani
eu Basque
be Belarusian
bn Bengali
bs Bosnian
bg Bulgarian
ca Catalan
ceb Cebuano
ny Chichewa
zh-CN Chinese (Simplified)
zh-TW Chinese (Traditional)
co Corsican
hr Croatian
cs Czech
da Danish
nl Dutch
en English
eo Esperanto
et Estonian
tl Filipino
fi Finnish
fr French
fy Frisian
gl Galician
ka Georgian
de German
el Greek
gu Gujarati
ht Haitian Creole
ha Hausa
haw Hawaiian
iw Hebrew
hi Hindi
hmn Hmong
hu Hungarian
is Icelandic
ig Igbo
id Indonesian
ga Irish
it Italian
ja Japanese
jw Javanese
kn Kannada
kk Kazakh
km Khmer
ko Korean
ku Kurdish (Kurmanji)
ky Kyrgyz
lo Lao
la Latin
lv Latvian
lt Lithuanian
lb Luxembourgish
mk Macedonian
mg Malagasy
ms Malay
ml Malayalam
mt Maltese
mi Maori
mr Marathi
mn Mongolian
my Myanmar (Burmese)
ne Nepali
no Norwegian
ps Pashto
fa Persian Download
pl Polish
pt Portuguese
pa Punjabi
ro Romanian
ru Russian
sm Samoan
gd Scots Gaelic
sr Serbian
st Sesotho
sn Shona
sd Sindhi
si Sinhala
sk Slovak
sl Slovenian
so Somali
es Spanish
su Sundanese
sw Swahili
sv Swedish
tg Tajik
ta Tamil
te Telugu
th Thai
tr Turkish
uk Ukrainian
ur Urdu
uz Uzbek
vi Vietnamese
cy Welsh
xh Xhosa
yi Yiddish
yo Yoruba
zu Zulu
or Odia (Oriya)
rw Kinyarwanda
tk Turkmen
tt Tatar
ug Uyghur
Would you like to inspect the original subtitles? These are the user uploaded subtitles that are being translated: 1 00:00:01,010 --> 00:00:03,730 And now to finish this project, 2 00:00:03,730 --> 00:00:06,593 let's finish this Stripe integration with webhooks. 3 00:00:09,310 --> 00:00:12,040 Let's start to remember how our Stripe integration 4 00:00:12,040 --> 00:00:14,210 actually works right now. 5 00:00:14,210 --> 00:00:16,750 We have this checkout-session endpoint, 6 00:00:16,750 --> 00:00:19,540 which gets called from our front-end. 7 00:00:19,540 --> 00:00:22,293 This will then call the getCheckoutSession function, 8 00:00:23,440 --> 00:00:25,100 so basically this one here, 9 00:00:25,100 --> 00:00:28,180 which will create a checkout session on the server 10 00:00:28,180 --> 00:00:30,300 using all of this information here, 11 00:00:30,300 --> 00:00:32,750 and then send it back to the client. 12 00:00:32,750 --> 00:00:35,170 Then after the processing of the payment 13 00:00:35,170 --> 00:00:37,280 is successfully done by Stripe, 14 00:00:37,280 --> 00:00:40,990 then redirect the user to this success URL, 15 00:00:40,990 --> 00:00:42,483 so this one that we created. 16 00:00:44,210 --> 00:00:48,120 Remember that onto this URL, we added the tour ID, 17 00:00:48,120 --> 00:00:50,920 the user ID, and also the price. 18 00:00:50,920 --> 00:00:55,040 We did that so that once this URL here is then called, 19 00:00:55,040 --> 00:00:57,920 our application would create a new booking document 20 00:00:57,920 --> 00:00:59,680 in our database. 21 00:00:59,680 --> 00:01:01,047 How did that work? 22 00:01:01,047 --> 00:01:04,742 In the my-tours route, we have a middleware for that. 23 00:01:06,040 --> 00:01:09,940 Remember, here in the viewRoutes, in my-tours, 24 00:01:09,940 --> 00:01:12,467 we have this createBookingCheckout. 25 00:01:14,770 --> 00:01:18,628 This function here, which basically from the query 26 00:01:18,628 --> 00:01:21,440 takes the tour, user, and price, 27 00:01:21,440 --> 00:01:25,023 and creates a entry in the database using that data. 28 00:01:26,350 --> 00:01:29,160 Basically we put this data on the URL 29 00:01:29,160 --> 00:01:32,500 whenever Stripe successfully processes a payment. 30 00:01:32,500 --> 00:01:34,990 And then this middleware function that we have here 31 00:01:34,990 --> 00:01:38,570 picks up the data and creates a new booking in our system 32 00:01:38,570 --> 00:01:39,960 using that data. 33 00:01:39,960 --> 00:01:42,790 And then after that, we basically redirect here 34 00:01:42,790 --> 00:01:45,763 onto the original URL without the query string. 35 00:01:46,770 --> 00:01:50,150 Now the problem with this was that it's not really secure. 36 00:01:50,150 --> 00:01:52,963 So, everyone who knows this URL structure, 37 00:01:54,010 --> 00:01:57,670 so this one up here, which tour, user, and price 38 00:01:57,670 --> 00:02:00,700 in the query string, can basically create a booking 39 00:02:01,761 --> 00:02:03,850 in our system without actually paying. 40 00:02:03,850 --> 00:02:07,120 So, all that we'd have to do is to open up this URL 41 00:02:07,120 --> 00:02:08,500 with some data in there 42 00:02:08,500 --> 00:02:11,680 and then from there automatically create a booking 43 00:02:11,680 --> 00:02:14,193 without going through the Stripe process. 44 00:02:15,540 --> 00:02:18,630 Remember how back then I said that we would fix this 45 00:02:18,630 --> 00:02:20,853 using something called webhooks. 46 00:02:22,090 --> 00:02:23,120 So, we do that now. 47 00:02:23,120 --> 00:02:24,090 Because for that, 48 00:02:24,090 --> 00:02:27,140 we actually need our website to be deployed. 49 00:02:27,140 --> 00:02:29,350 Now at this point, that's actually the case. 50 00:02:29,350 --> 00:02:31,833 And so, now we can implement these webhooks. 51 00:02:33,240 --> 00:02:35,663 For that, let's go to our Stripe dashboard. 52 00:02:37,400 --> 00:02:39,750 And I actually already have that opened here. 53 00:02:39,750 --> 00:02:43,903 And then go here to developers, then choose webhooks, 54 00:02:45,070 --> 00:02:47,970 and here, add a new endpoint. 55 00:02:47,970 --> 00:02:52,149 Now what is this endpoint here and this webhook? 56 00:02:52,149 --> 00:02:55,380 Basically we're gonna specify a URL here 57 00:02:55,380 --> 00:02:59,500 to which Stripe will automatically send a POST request to 58 00:02:59,500 --> 00:03:02,800 whenever a checkout session has successfully completed, 59 00:03:02,800 --> 00:03:05,740 so basically whenever a payment was successful. 60 00:03:05,740 --> 00:03:09,920 With that POST request, Stripe will then send back 61 00:03:09,920 --> 00:03:13,230 the original session data that we created in the first step 62 00:03:13,230 --> 00:03:15,623 when we created that checkout session. 63 00:03:17,540 --> 00:03:20,130 That's the reason why we actually needed our website 64 00:03:20,130 --> 00:03:23,010 to be deployed here because now we need to specify 65 00:03:23,010 --> 00:03:24,923 that real-life URL here. 66 00:03:27,170 --> 00:03:28,573 Let's grab that from here, 67 00:03:31,290 --> 00:03:34,150 and then add our route here basically. 68 00:03:34,150 --> 00:03:36,930 I'm going to call this one webhook-checkout. 69 00:03:41,620 --> 00:03:45,350 It's not in the API, and it's not inside the bookings. 70 00:03:45,350 --> 00:03:47,593 You will see in a moment why that is. 71 00:03:49,130 --> 00:03:51,210 Again, when a payment was successful, 72 00:03:51,210 --> 00:03:53,280 Stripe will then automatically post 73 00:03:53,280 --> 00:03:55,503 the original session data to this URL. 74 00:03:58,060 --> 00:04:00,380 Now we also need to select the event. 75 00:04:00,380 --> 00:04:04,740 And you see there are tons of events that we could use here. 76 00:04:04,740 --> 00:04:09,667 The one that we're using is the checkout_session_completed. 77 00:04:11,767 --> 00:04:12,650 Add that. 78 00:04:12,650 --> 00:04:15,083 Now you need to put in your password here again. 79 00:04:17,100 --> 00:04:19,110 And then there we go. 80 00:04:19,110 --> 00:04:22,665 This webhook then also has a secret here. 81 00:04:22,665 --> 00:04:25,850 This one, we will then also need in a second 82 00:04:25,850 --> 00:04:29,063 when we actually create our route for this URL here. 83 00:04:29,980 --> 00:04:32,430 Actually that's exactly what we're gonna do next. 84 00:04:33,750 --> 00:04:35,600 Basically in our system of course, 85 00:04:35,600 --> 00:04:38,840 we now need a route for this here 86 00:04:39,960 --> 00:04:43,840 so that when Stripe then posts the data to our application, 87 00:04:43,840 --> 00:04:46,233 we can then actually do something with it. 88 00:04:48,120 --> 00:04:52,233 Let's go back here and open up our application. 89 00:04:54,740 --> 00:04:57,743 We will actually add this route right here. 90 00:04:59,610 --> 00:05:03,100 Again, I will explain you why in a second. 91 00:05:03,100 --> 00:05:04,350 So, app.post 92 00:05:06,320 --> 00:05:08,850 and standard route and then of course we need 93 00:05:08,850 --> 00:05:10,810 a handler function for that. 94 00:05:10,810 --> 00:05:14,720 Let's quickly create it here in our bookingController. 95 00:05:14,720 --> 00:05:19,013 Let me call that export.webhookCheckout. 96 00:05:31,360 --> 00:05:36,360 Now I will have to import this controller into app.js. 97 00:05:39,210 --> 00:05:42,110 Let's do that right here after the bookingRouter actually, 98 00:05:45,150 --> 00:05:47,133 so this one and this one, 99 00:05:49,440 --> 00:05:51,383 controller and here also controller. 100 00:05:54,580 --> 00:05:56,050 All right. 101 00:05:56,050 --> 00:06:01,050 Now down here, that's bookingController.webhookCheckout. 102 00:06:04,800 --> 00:06:08,820 Now why do we actually define this webhook-checkout 103 00:06:08,820 --> 00:06:12,410 right here in app.js instead of doing it for example 104 00:06:12,410 --> 00:06:14,440 in the bookingRouter. 105 00:06:14,440 --> 00:06:17,950 The reason for that is that in this handler function, 106 00:06:17,950 --> 00:06:20,677 when we receive the body from Stripe, 107 00:06:20,677 --> 00:06:22,850 the Stripe function that we're then gonna use 108 00:06:22,850 --> 00:06:26,780 to actually read the body needs this body in a raw form, 109 00:06:26,780 --> 00:06:29,633 so basically as a string and not as JSON. 110 00:06:31,370 --> 00:06:34,140 Again, in this route here, we need the body 111 00:06:34,140 --> 00:06:37,555 coming with the request to be not in JSON, 112 00:06:37,555 --> 00:06:40,600 otherwise this is not going to be working at all. 113 00:06:40,600 --> 00:06:43,700 Now the thing is, that as soon as a request 114 00:06:43,700 --> 00:06:46,710 hits this middleware here, the body will be parsed 115 00:06:46,710 --> 00:06:48,563 and converted to JSON. 116 00:06:49,700 --> 00:06:54,650 It will then be put on request.body as a simple JSON object. 117 00:06:54,650 --> 00:06:57,520 Again with that, this route handler here 118 00:06:57,520 --> 00:06:59,180 would then not work. 119 00:06:59,180 --> 00:07:02,520 That's the whole reason why we need to put this route here 120 00:07:02,520 --> 00:07:04,557 before we call the body-parser. 121 00:07:05,580 --> 00:07:08,260 Now we still need to actually parse the body 122 00:07:08,260 --> 00:07:10,120 but in a so-called raw format. 123 00:07:10,120 --> 00:07:13,690 By the time I was recording this video, 124 00:07:13,690 --> 00:07:17,220 we could not do it with Express out of the box. 125 00:07:17,220 --> 00:07:21,500 And so, in this video, we download the body-parser from npm 126 00:07:21,500 --> 00:07:24,220 and use it as I show it in the video. 127 00:07:24,220 --> 00:07:28,340 However, like five days after I recorded this video, 128 00:07:28,340 --> 00:07:32,770 Express added the raw parser to Express as well. 129 00:07:32,770 --> 00:07:37,000 Now we can use express.raw instead of having to install 130 00:07:37,000 --> 00:07:39,523 the body-parser or middleware from npm. 131 00:07:40,530 --> 00:07:44,610 Again, in this video, I will now install the body-parser, 132 00:07:44,610 --> 00:07:46,440 but you don't really need to. 133 00:07:46,440 --> 00:07:49,293 You can just use express.raw instead. 134 00:07:51,590 --> 00:07:52,700 Npm install 135 00:07:54,480 --> 00:07:55,403 body-parser. 136 00:07:58,950 --> 00:08:02,120 This probably all sounds a little bit focusing, 137 00:08:02,120 --> 00:08:04,350 and I totally understand that, 138 00:08:04,350 --> 00:08:08,050 but that's just how the Stripe documentation works 139 00:08:08,890 --> 00:08:10,893 and forces us to do it, really. 140 00:08:15,210 --> 00:08:17,100 Let's go back here to our route. 141 00:08:17,100 --> 00:08:20,453 In this route, we need the body to be in a raw format. 142 00:08:21,460 --> 00:08:25,330 We can add that as a middleware here between the route 143 00:08:25,330 --> 00:08:26,673 and the final handler. 144 00:08:28,654 --> 00:08:31,013 Here we say bodyParser.raw, 145 00:08:34,830 --> 00:08:37,490 and we also need to specific here the type 146 00:08:39,450 --> 00:08:43,126 just very quick as application/json. 147 00:08:48,130 --> 00:08:52,660 We now added this body parsing as a raw body 148 00:08:52,660 --> 00:08:54,183 here in this middleware stack. 149 00:08:55,964 --> 00:08:58,150 All this will really start to come together 150 00:08:58,150 --> 00:09:00,970 once we start implementing this function. 151 00:09:00,970 --> 00:09:02,543 Actually let's do that now, 152 00:09:03,820 --> 00:09:05,210 so right here. 153 00:09:05,210 --> 00:09:07,100 But before we actually do that, 154 00:09:07,100 --> 00:09:09,780 let's get rid of all the code that we wrote 155 00:09:09,780 --> 00:09:11,680 in order to make it work right now. 156 00:09:11,680 --> 00:09:14,420 So, basically this middleware function, 157 00:09:14,420 --> 00:09:16,350 we don't need it anymore. 158 00:09:16,350 --> 00:09:18,480 Also here in the viewRoutes, 159 00:09:18,480 --> 00:09:21,980 we don't need it here anymore either. 160 00:09:21,980 --> 00:09:24,770 And then finally in the bookingController, 161 00:09:24,770 --> 00:09:28,153 let's also set our URL back to normal. 162 00:09:31,080 --> 00:09:33,180 I will just leave all of this here 163 00:09:33,180 --> 00:09:35,233 so that you can keep it as a reference. 164 00:09:37,390 --> 00:09:40,863 But now the success URL should actually just be this. 165 00:09:43,090 --> 00:09:45,400 Basically after a successful booking, 166 00:09:45,400 --> 00:09:48,090 we still want to come back to my-tours 167 00:09:48,090 --> 00:09:50,350 but without all this query parameters 168 00:09:51,350 --> 00:09:54,580 because now it's no longer this function here, 169 00:09:54,580 --> 00:09:57,430 which will take care of creating the booking 170 00:09:57,430 --> 00:09:59,770 but instead it is this function here, 171 00:09:59,770 --> 00:10:02,060 which is of course the one that gets called 172 00:10:02,060 --> 00:10:05,633 once Stripe calls our webhook. 173 00:10:07,140 --> 00:10:08,470 Let's now implement this. 174 00:10:08,470 --> 00:10:10,140 The first thing that we need to do 175 00:10:10,140 --> 00:10:13,763 is to rid this Stripe signature out of our headers, 176 00:10:15,780 --> 00:10:19,840 so signature and then request.headers 177 00:10:21,500 --> 00:10:26,373 and then from there stripe-signature. 178 00:10:28,220 --> 00:10:30,710 Basically when Stripe calls our webhook, 179 00:10:30,710 --> 00:10:32,830 it will add a header to that request 180 00:10:32,830 --> 00:10:36,280 containing a special signature for our webhook. 181 00:10:38,480 --> 00:10:40,700 If you're thinking that you're just blindly following 182 00:10:40,700 --> 00:10:42,590 what I'm doing here, well, (laughs) 183 00:10:42,590 --> 00:10:45,070 that's actually exactly what I did as well 184 00:10:45,070 --> 00:10:47,050 from the Stripe documentations. 185 00:10:47,050 --> 00:10:50,320 Again, this is really just how Stripe works, 186 00:10:50,320 --> 00:10:52,973 and there's nothing we can do against that. 187 00:10:54,350 --> 00:10:57,453 Anyway, next up, let's create a Stripe event, 188 00:10:59,310 --> 00:11:03,690 so const event equals stripe. 189 00:11:03,690 --> 00:11:07,410 For that of course, we need to the Stripe library installed, 190 00:11:07,410 --> 00:11:09,573 which we have up here. 191 00:11:12,650 --> 00:11:14,350 So, stripe.webhooks.contructEvent. 192 00:11:20,378 --> 00:11:23,210 Now here is where finally that body 193 00:11:23,210 --> 00:11:26,520 comes into play, so request.body. 194 00:11:26,520 --> 00:11:28,370 And remember that this body here 195 00:11:28,370 --> 00:11:30,220 needs to be in the raw form, 196 00:11:30,220 --> 00:11:32,083 so basically available as a string. 197 00:11:33,130 --> 00:11:36,340 Once more, that is why we put that route 198 00:11:36,340 --> 00:11:38,110 before all our other routes 199 00:11:38,110 --> 00:11:41,580 and especially before the body parser could do its job 200 00:11:41,580 --> 00:11:44,863 of converting our body into a JSON object. 201 00:11:46,170 --> 00:11:51,050 Then besides that body, for the event, we need a signature, 202 00:11:51,050 --> 00:11:53,370 so basically the signature that was sent 203 00:11:53,370 --> 00:11:56,763 along with the header, and then finally our webhook secret. 204 00:11:57,710 --> 00:12:00,653 Let's get that from here, copy it. 205 00:12:01,585 --> 00:12:05,610 Since it's a secret, we should, as always, add it here 206 00:12:05,610 --> 00:12:07,143 to our config file, 207 00:12:10,460 --> 00:12:12,737 so STRIPE_WEBHOOK_SECRET. 208 00:12:16,650 --> 00:12:19,380 And then later of course, don't forget to also add this 209 00:12:19,380 --> 00:12:21,663 to our Heroku configuration. 210 00:12:26,100 --> 00:12:27,330 Let's now use that. 211 00:12:27,330 --> 00:12:28,767 Add process.env. 212 00:12:30,330 --> 00:12:31,830 I should have just copied that 213 00:12:35,690 --> 00:12:36,573 right here. 214 00:12:37,752 --> 00:12:41,200 So, you see, all of this is really to make the process 215 00:12:41,200 --> 00:12:43,450 super, super secure. 216 00:12:43,450 --> 00:12:45,970 We need all of this data like the signature 217 00:12:45,970 --> 00:12:49,450 and also the secret in order to basically validate 218 00:12:49,450 --> 00:12:51,640 the data that comes in the body 219 00:12:51,640 --> 00:12:54,433 so that no one can actually manipulate that. 220 00:12:55,870 --> 00:12:58,050 Now during the creation of this event, 221 00:12:58,050 --> 00:12:59,280 there might be some errors, 222 00:12:59,280 --> 00:13:01,420 for example if the signature is wrong 223 00:13:01,420 --> 00:13:03,900 or if the secret is wrong. 224 00:13:03,900 --> 00:13:07,813 And so, let's wrap this into a try-catch block. 225 00:13:16,290 --> 00:13:17,850 Okay. 226 00:13:17,850 --> 00:13:19,500 Of course, we now need the catch. 227 00:13:22,150 --> 00:13:23,410 In case there is an error, 228 00:13:23,410 --> 00:13:26,053 we want to send back an error to Stripe, 229 00:13:27,880 --> 00:13:32,450 so return res.status 400 230 00:13:33,756 --> 00:13:35,657 and then just use send webhook error 231 00:13:40,140 --> 00:13:44,023 and then let's just add the error.message. 232 00:13:45,714 --> 00:13:49,220 So, it is Stripe who will receive this response here 233 00:13:49,220 --> 00:13:53,230 because again it is Stripe who will actually call the URL, 234 00:13:53,230 --> 00:13:56,603 so our webhook, which will then call this function. 235 00:13:58,520 --> 00:14:02,420 Now we need to of course also declare this event here 236 00:14:02,420 --> 00:14:04,610 outside of the try-catch block 237 00:14:04,610 --> 00:14:07,623 because otherwise we will not be able to use it down there. 238 00:14:08,660 --> 00:14:13,160 So, let event and then reassign down here 239 00:14:13,160 --> 00:14:15,430 because remember that the ES6 const 240 00:14:15,430 --> 00:14:17,450 and let are block-scoped. 241 00:14:17,450 --> 00:14:20,480 And so, this variable would not be available outside 242 00:14:20,480 --> 00:14:21,473 of this block. 243 00:14:23,180 --> 00:14:25,830 Now let's actually use that event. 244 00:14:25,830 --> 00:14:29,090 First off, we need to test if this really is the event 245 00:14:29,090 --> 00:14:29,923 that we want. 246 00:14:30,810 --> 00:14:34,240 So, we can do event.type 247 00:14:34,240 --> 00:14:38,973 is equal to checkout.session.complete. 248 00:14:42,080 --> 00:14:44,370 Remember that in our Stripe dashboard, 249 00:14:44,370 --> 00:14:48,090 that's exactly the type that we defined here. 250 00:14:48,090 --> 00:14:49,260 So, that's the event type. 251 00:14:49,260 --> 00:14:52,183 Now we're checking if that is really the event 252 00:14:52,183 --> 00:14:56,287 that we are receiving here just to be 100% sure. 253 00:14:56,287 --> 00:14:59,780 If it is, we then want to actually use the event 254 00:14:59,780 --> 00:15:02,053 to create our booking in our database. 255 00:15:03,860 --> 00:15:06,280 Actually let's do that in a separate function 256 00:15:06,280 --> 00:15:08,983 and not inside here of all of this mess. 257 00:15:10,517 --> 00:15:12,590 For that, I will create a function. 258 00:15:12,590 --> 00:15:13,640 Actually let me give it 259 00:15:13,640 --> 00:15:15,990 this exact same name, so createBookingCheckout. 260 00:15:17,487 --> 00:15:19,490 It was actually a nice name, 261 00:15:19,490 --> 00:15:21,450 but now it cannot be a middleware 262 00:15:21,450 --> 00:15:23,250 but instead just a regular function. 263 00:15:26,080 --> 00:15:28,823 This function will accept the session data. 264 00:15:31,080 --> 00:15:35,310 And remember that the session data is exactly this session 265 00:15:35,310 --> 00:15:37,513 that we created here in the first place. 266 00:15:41,404 --> 00:15:43,730 If this is the correct event, 267 00:15:43,730 --> 00:15:45,743 then let's actually call that function, 268 00:15:46,680 --> 00:15:49,500 so createBookingCheckout with the session, 269 00:15:49,500 --> 00:15:53,057 which is at event.data.object. 270 00:15:57,447 --> 00:15:58,320 And then finally, 271 00:15:58,320 --> 00:16:01,333 let's just send back some response to Stripe. 272 00:16:02,450 --> 00:16:03,840 So, status 200 273 00:16:05,780 --> 00:16:07,480 and then let's say some json 274 00:16:10,300 --> 00:16:11,823 receive set to true. 275 00:16:13,200 --> 00:16:14,033 Makes sense? 276 00:16:16,000 --> 00:16:18,490 Once more, all of this code here will run 277 00:16:18,490 --> 00:16:21,390 whenever a payment was successful. 278 00:16:21,390 --> 00:16:25,380 Stripe will then call our webhook, which is the URL, 279 00:16:25,380 --> 00:16:27,420 which is going to call this function. 280 00:16:27,420 --> 00:16:30,600 And so, this function receives a body from the request, 281 00:16:30,600 --> 00:16:34,330 and then together with the signature and/or webhook secret, 282 00:16:34,330 --> 00:16:37,110 creates an event, which will contain the session. 283 00:16:37,110 --> 00:16:39,190 And then using that session data, 284 00:16:39,190 --> 00:16:41,963 we can create our new booking in the database. 285 00:16:43,987 --> 00:16:45,660 And so, that will actually be pretty similar 286 00:16:45,660 --> 00:16:47,143 to what we had here before. 287 00:16:48,400 --> 00:16:51,790 So, we will need this line of code here again. 288 00:16:51,790 --> 00:16:53,923 So, this will also be an async function. 289 00:16:58,497 --> 00:17:00,530 And so, this is exactly the same. 290 00:17:00,530 --> 00:17:02,260 Now what we need here of course 291 00:17:02,260 --> 00:17:06,690 is to get access to the tour, user, and price. 292 00:17:06,690 --> 00:17:10,550 But that data once more is stored in that session. 293 00:17:10,550 --> 00:17:12,400 So, let's start with the tour. 294 00:17:12,400 --> 00:17:14,780 And remember how up here 295 00:17:14,780 --> 00:17:17,099 when we first created this handler function, 296 00:17:17,099 --> 00:17:20,040 I specified this client_reference_id field 297 00:17:20,040 --> 00:17:22,369 and added the tourId to that. 298 00:17:22,369 --> 00:17:23,839 Remember that? 299 00:17:23,839 --> 00:17:25,700 I did that because, at the time, 300 00:17:25,700 --> 00:17:29,840 I already knew that we would need this tour ID a bit later. 301 00:17:29,840 --> 00:17:32,490 Now it's that time where we actually need the tour ID 302 00:17:32,490 --> 00:17:35,333 in order to be able to create that new booking. 303 00:17:36,732 --> 00:17:38,490 And so, now the tour ID that we need 304 00:17:38,490 --> 00:17:41,670 is at session dot client's reference ID. 305 00:17:41,670 --> 00:17:44,770 So, let's copy this and say 306 00:17:47,870 --> 00:17:48,703 session. 307 00:17:49,660 --> 00:17:53,823 And of course, here we need to say tour. 308 00:17:55,670 --> 00:17:57,040 So, that's the tour ID. 309 00:17:57,040 --> 00:17:59,150 Next up, we need the user ID. 310 00:17:59,150 --> 00:18:01,240 Now the information that we have in our session 311 00:18:01,240 --> 00:18:03,973 about the user is the user's email. 312 00:18:05,630 --> 00:18:07,170 And so, now what we need to do 313 00:18:07,170 --> 00:18:10,500 is to basically get the user's ID. 314 00:18:10,500 --> 00:18:12,793 For that, we will query by the email. 315 00:18:13,720 --> 00:18:16,810 That's no problem because the email is also unique. 316 00:18:16,810 --> 00:18:19,353 Based on that, we can then find the unique ID. 317 00:18:20,370 --> 00:18:24,183 So, const user is await. 318 00:18:25,570 --> 00:18:27,660 And I think we already have the user here. 319 00:18:27,660 --> 00:18:28,493 No? 320 00:18:29,520 --> 00:18:30,570 No, I actually don't. 321 00:18:31,890 --> 00:18:33,290 So, let's just do that here. 322 00:18:35,490 --> 00:18:37,973 And user here as well. 323 00:18:41,070 --> 00:18:41,903 Okay. 324 00:18:41,903 --> 00:18:46,890 So, User.findOne and then query via email, 325 00:18:47,990 --> 00:18:51,330 which is in session dot, 326 00:18:51,330 --> 00:18:53,780 and I believe that's client's email or something. 327 00:18:55,200 --> 00:18:56,200 It's customer_email. 328 00:18:59,860 --> 00:19:00,693 Okay. 329 00:19:02,070 --> 00:19:04,970 But that will then return the entire document, 330 00:19:04,970 --> 00:19:06,910 but we want actually the ID. 331 00:19:06,910 --> 00:19:09,780 So, let's wrap all of this here in parenthesis 332 00:19:10,730 --> 00:19:14,743 and then call the ID on there or actually read the ID. 333 00:19:16,620 --> 00:19:17,960 So, that's it. 334 00:19:17,960 --> 00:19:19,233 And finally, the price. 335 00:19:22,350 --> 00:19:24,023 Where is the price stored? 336 00:19:25,320 --> 00:19:26,833 Well, it's here in line_items. 337 00:19:27,880 --> 00:19:30,610 That's an array, so at element zero, 338 00:19:30,610 --> 00:19:33,553 and then the amount divided by 100. 339 00:19:34,580 --> 00:19:38,210 So, we multiplied it here by 100 to get cents, 340 00:19:38,210 --> 00:19:41,590 but now of course we want it back in dollars. 341 00:19:41,590 --> 00:19:44,700 So, we need to basically divide that back. 342 00:19:44,700 --> 00:19:48,550 And so, session.line_items 343 00:19:49,460 --> 00:19:54,460 and then the first element dot amount if I'm right. 344 00:19:55,950 --> 00:19:56,783 Yeah. 345 00:19:56,783 --> 00:20:01,710 So, amount divided by 100. 346 00:20:01,710 --> 00:20:04,010 That should actually be it. 347 00:20:04,010 --> 00:20:06,630 Let's now commit our changes to the repo 348 00:20:06,630 --> 00:20:08,740 and push it to Stripe. 349 00:20:08,740 --> 00:20:12,840 So, git add all, of course, 350 00:20:12,840 --> 00:20:16,600 and then git commit message 351 00:20:18,090 --> 00:20:21,633 Improved stripe implementation, 352 00:20:24,960 --> 00:20:29,960 and then git push heroku master. 353 00:20:31,190 --> 00:20:33,273 Once more, this will take some time. 354 00:20:33,273 --> 00:20:35,263 I'll see you when that's done. 355 00:20:36,200 --> 00:20:37,033 All right. 356 00:20:37,033 --> 00:20:40,323 Now don't forget to set that new environment variable. 357 00:20:41,610 --> 00:20:46,610 So, that's heroku config colon set, 358 00:20:46,750 --> 00:20:49,433 and then simply copy it from here. 359 00:20:53,590 --> 00:20:54,720 Okay. 360 00:20:54,720 --> 00:20:56,800 That then restarts the application. 361 00:20:56,800 --> 00:20:58,173 And that's it. 362 00:20:59,570 --> 00:21:02,723 So, let's now actually go ahead and test it. 363 00:21:04,980 --> 00:21:05,813 All right. 364 00:21:07,050 --> 00:21:09,480 We are still here in our application. 365 00:21:09,480 --> 00:21:12,883 Let's see which tours Laura has already booked. 366 00:21:14,100 --> 00:21:15,370 She has the Forest Hiker. 367 00:21:15,370 --> 00:21:19,823 That booking was still done using the old method. 368 00:21:21,050 --> 00:21:24,240 But that old method now no longer works. 369 00:21:24,240 --> 00:21:27,047 Now if we do another booking and it works, 370 00:21:27,047 --> 00:21:29,490 well, then that's going to mean 371 00:21:29,490 --> 00:21:32,773 that of course our new implementation works. 372 00:21:34,730 --> 00:21:35,780 Let's test that here. 373 00:21:39,760 --> 00:21:41,493 As always, 4242. 374 00:21:50,420 --> 00:21:51,683 Now let's wait for it. 375 00:21:52,730 --> 00:21:55,740 Well, that apparently didn't go so well 376 00:21:55,740 --> 00:21:58,520 because otherwise our second new tour 377 00:21:58,520 --> 00:22:00,743 should already be here in our bookings. 378 00:22:02,230 --> 00:22:04,203 Let's see here in our dashboard. 379 00:22:05,860 --> 00:22:06,983 If we now reload this, 380 00:22:12,150 --> 00:22:15,893 then we actually see that there was a successful event. 381 00:22:17,407 --> 00:22:20,320 So, that's the event that we just created 382 00:22:20,320 --> 00:22:23,170 and which sent this body here 383 00:22:23,170 --> 00:22:25,380 and then received this response. 384 00:22:25,380 --> 00:22:27,560 So, this receive set to true 385 00:22:27,560 --> 00:22:30,663 is exactly what we did here in our code, 386 00:22:31,670 --> 00:22:32,633 so this here. 387 00:22:34,060 --> 00:22:36,000 So, that's the response that we sent 388 00:22:36,000 --> 00:22:39,770 and the body that we got in was all of this data. 389 00:22:39,770 --> 00:22:42,810 And so, we can see here the session with the price, 390 00:22:42,810 --> 00:22:46,460 with the email, with the tour. 391 00:22:46,460 --> 00:22:49,483 And so, I'm not sure why it didn't work. 392 00:22:51,000 --> 00:22:53,163 So, let's just quickly reload this here. 393 00:22:55,780 --> 00:22:59,050 So, actually our Stripe implementation should be correct, 394 00:22:59,050 --> 00:23:02,013 but, for some reason, our new booking was not created. 395 00:23:03,120 --> 00:23:05,020 Let's check that also here in Compass. 396 00:23:07,460 --> 00:23:09,970 And indeed, it's not there. 397 00:23:09,970 --> 00:23:12,123 So, let's go back to our code here. 398 00:23:13,410 --> 00:23:17,360 Oh and one error that I see right away is here. 399 00:23:17,360 --> 00:23:20,393 So, it should be completed like this. 400 00:23:22,090 --> 00:23:24,480 So, that's a stupid mistake. 401 00:23:24,480 --> 00:23:26,950 Let's just see if there might be another error 402 00:23:26,950 --> 00:23:30,050 up here in createBookingCheckout. 403 00:23:30,050 --> 00:23:30,883 Here we have 404 00:23:32,750 --> 00:23:33,583 line_items. 405 00:23:33,583 --> 00:23:35,093 Let's see if that's correct. 406 00:23:36,110 --> 00:23:38,170 And yeah, it seems to be. 407 00:23:38,170 --> 00:23:41,123 We can also confirm that here again in our Stripe. 408 00:23:43,110 --> 00:23:45,290 Actually here it's called display_items. 409 00:23:46,590 --> 00:23:47,423 That's weird. 410 00:23:48,367 --> 00:23:52,140 Just to make sure, let's also call it display_items 411 00:23:52,140 --> 00:23:54,363 here in our code right here. 412 00:23:55,980 --> 00:23:57,580 Now another thing that I noticed 413 00:23:58,750 --> 00:24:00,350 now as I took another look here 414 00:24:00,350 --> 00:24:03,510 is that we still have this image hardcoded 415 00:24:03,510 --> 00:24:05,763 to this other natours.dev. 416 00:24:07,587 --> 00:24:11,380 Now let's actually fix that because at this point of course, 417 00:24:11,380 --> 00:24:14,580 our website is already live and deployed. 418 00:24:14,580 --> 00:24:16,600 And so, we can basically replace that 419 00:24:16,600 --> 00:24:18,100 with the same as we have here. 420 00:24:20,900 --> 00:24:23,430 So, we use this part here many times. 421 00:24:23,430 --> 00:24:25,480 And so, it's time to use that again here. 422 00:24:32,672 --> 00:24:33,505 Yeah. 423 00:24:33,505 --> 00:24:35,353 Let's try to redeploy this. 424 00:24:36,380 --> 00:24:38,113 So, git add all again. 425 00:24:40,420 --> 00:24:42,070 And let's just call this here 426 00:24:42,070 --> 00:24:44,430 Improved stripe implementation two. 427 00:24:44,430 --> 00:24:47,693 And then push it again to Heroku. 428 00:24:51,580 --> 00:24:52,560 Okay. 429 00:24:52,560 --> 00:24:54,253 Let's try that one more time. 430 00:24:55,830 --> 00:24:57,023 Let's go back here. 431 00:25:00,630 --> 00:25:04,063 Now let's try to book again to Park Camper. 432 00:25:15,760 --> 00:25:16,683 All right. 433 00:25:17,920 --> 00:25:21,530 You ought to see the image popping up here on the left side. 434 00:25:21,530 --> 00:25:24,200 That means that our new image integration 435 00:25:24,200 --> 00:25:25,753 also worked just fine. 436 00:25:27,220 --> 00:25:28,283 Now it's processing. 437 00:25:29,382 --> 00:25:31,380 Ah now it is here. 438 00:25:31,380 --> 00:25:32,320 Great. 439 00:25:32,320 --> 00:25:33,533 That's beautiful. 440 00:25:34,420 --> 00:25:36,850 Now we really have a secure 441 00:25:36,850 --> 00:25:39,940 and way more professional Stripe implementation 442 00:25:39,940 --> 00:25:41,173 in our application. 443 00:25:42,070 --> 00:25:43,520 That's great. 444 00:25:43,520 --> 00:25:45,570 Of course, if you reload here, 445 00:25:45,570 --> 00:25:49,500 then you should see this new event here, 446 00:25:49,500 --> 00:25:52,050 so this new call to our webhook, 447 00:25:52,050 --> 00:25:54,593 which of course again was successful. 448 00:25:55,840 --> 00:25:57,690 That's just great. 449 00:25:57,690 --> 00:26:00,740 Now there's just one final thing that I want to do, 450 00:26:00,740 --> 00:26:04,420 which is to basically give the user some feedback 451 00:26:04,420 --> 00:26:06,980 in form of one of these green messages 452 00:26:06,980 --> 00:26:09,123 that we use also for example in the login. 453 00:26:10,650 --> 00:26:12,930 Right now our application doesn't really give 454 00:26:12,930 --> 00:26:16,476 any kind of feedback when a new tour was booked. 455 00:26:16,476 --> 00:26:18,650 Now I want to change that. 456 00:26:18,650 --> 00:26:21,900 However, doing this is not really straightforward 457 00:26:21,900 --> 00:26:23,990 because remember that these messages 458 00:26:23,990 --> 00:26:26,750 are actually displayed by JavaScript. 459 00:26:26,750 --> 00:26:30,280 So, in the other cases, we did an HTTP call to our API. 460 00:26:30,280 --> 00:26:33,070 And then when that was done, we used JavaScript 461 00:26:33,070 --> 00:26:34,840 to display some kind of message. 462 00:26:34,840 --> 00:26:36,970 But now we do not do it this way. 463 00:26:36,970 --> 00:26:40,710 And so, the message should already be somewhere in the HTML 464 00:26:40,710 --> 00:26:42,380 as soon as the page loads 465 00:26:42,380 --> 00:26:45,400 so that then our JavaScript can pick that message up 466 00:26:45,400 --> 00:26:49,070 from the HTML and display it nicely up there 467 00:26:49,070 --> 00:26:50,463 in one of these banners. 468 00:26:51,610 --> 00:26:54,510 And so, the way I'm going to put these alerts 469 00:26:54,510 --> 00:26:58,223 in the HTML is once more by using a data property. 470 00:26:59,450 --> 00:27:03,000 Let's start by implementing this feature right there 471 00:27:03,000 --> 00:27:04,363 in our main template. 472 00:27:06,610 --> 00:27:09,273 That's here in views, base. 473 00:27:11,160 --> 00:27:13,630 I will actually add that alert message 474 00:27:13,630 --> 00:27:15,663 right onto the body element. 475 00:27:17,110 --> 00:27:19,963 Here we will have a data alert property, 476 00:27:21,860 --> 00:27:24,000 which should actually only be set 477 00:27:24,000 --> 00:27:26,563 if the alert variable is available here. 478 00:27:27,480 --> 00:27:31,460 So, let's use ES6, so a template string, 479 00:27:31,460 --> 00:27:35,060 and say if there is an alert, 480 00:27:35,060 --> 00:27:38,713 then use alert here, and else, an empty string. 481 00:27:39,980 --> 00:27:43,370 And so, this alert here will be the alert message 482 00:27:43,370 --> 00:27:47,230 that JavaScript will then pick up and display on the page. 483 00:27:47,230 --> 00:27:50,230 Now how does this alert message then actually end up 484 00:27:50,230 --> 00:27:52,513 as an alert variable here in our template? 485 00:27:53,360 --> 00:27:56,448 Well, I came up with a solution that is reusable 486 00:27:56,448 --> 00:27:59,250 so that we can use all over our application. 487 00:27:59,250 --> 00:28:01,840 That is that on the query string, 488 00:28:01,840 --> 00:28:03,890 we will add some alert keyword 489 00:28:03,890 --> 00:28:05,820 and then we will have a middleware, 490 00:28:05,820 --> 00:28:08,560 which will take that keyword from the URL 491 00:28:08,560 --> 00:28:10,910 and, according to the keyword that we put there, 492 00:28:10,910 --> 00:28:15,050 will then put a whole alert message on response.locals. 493 00:28:15,050 --> 00:28:19,000 And so, remember that everything that's on response.locals 494 00:28:19,000 --> 00:28:22,483 is then available as a variable in all of our templates. 495 00:28:23,450 --> 00:28:25,630 So, we actually used that before 496 00:28:25,630 --> 00:28:27,563 in our authController, I believe. 497 00:28:29,480 --> 00:28:32,567 Very quickly, let me show that to you. 498 00:28:33,530 --> 00:28:37,060 Right here, we said response.local.user 499 00:28:37,060 --> 00:28:39,074 and put the current user there. 500 00:28:39,074 --> 00:28:41,720 Then automatically in all templates, 501 00:28:41,720 --> 00:28:44,283 we have access to that user variable. 502 00:28:47,430 --> 00:28:50,070 So, let's now implement what I just said 503 00:28:50,070 --> 00:28:52,597 and starting with the URL. 504 00:28:54,330 --> 00:28:57,540 What I'm gonna do here is to actually add that query string 505 00:28:57,540 --> 00:28:59,097 here to the success URL. 506 00:28:59,970 --> 00:29:04,573 Here, I will say alert equal booking. 507 00:29:05,970 --> 00:29:10,310 Now I could, in all other URLS, also add some alert 508 00:29:10,310 --> 00:29:12,863 and then with a different keyword here, of course. 509 00:29:14,350 --> 00:29:18,100 And we will just do it here really for this booking. 510 00:29:18,100 --> 00:29:21,793 But again I created a kind of reusable solution here. 511 00:29:23,340 --> 00:29:27,470 Anyway, now in our routes, we need basically a middleware, 512 00:29:27,470 --> 00:29:29,920 which will run for all the requests. 513 00:29:29,920 --> 00:29:32,270 And it's that middleware, which will pick up the alert 514 00:29:32,270 --> 00:29:35,240 from the query string and put a alert message 515 00:29:35,240 --> 00:29:37,453 onto our response.locals. 516 00:29:41,457 --> 00:29:42,624 So, router.use 517 00:29:45,040 --> 00:29:48,233 viewsController.alerts. 518 00:29:50,290 --> 00:29:52,320 And so, this is a middleware function, 519 00:29:52,320 --> 00:29:56,200 which will basically run for each and every single request 520 00:29:56,200 --> 00:29:58,130 that's coming into this router, 521 00:29:58,130 --> 00:30:01,063 so basically for all the requests to our website. 522 00:30:02,370 --> 00:30:04,870 Now let's actually create that middleware 523 00:30:04,870 --> 00:30:06,020 in our viewsController. 524 00:30:10,460 --> 00:30:12,380 So, exports.alerts 525 00:30:14,480 --> 00:30:17,283 request, response, and next. 526 00:30:19,650 --> 00:30:20,730 And so, the alert 527 00:30:22,760 --> 00:30:26,300 is request.query.alert. 528 00:30:26,300 --> 00:30:29,873 And so, let's just use this structuring here once more. 529 00:30:32,020 --> 00:30:36,553 And then let's say if alert is equals to booking, 530 00:30:39,030 --> 00:30:42,653 so the alert that we put right here in the query string, 531 00:30:44,670 --> 00:30:46,070 well, then in that case, 532 00:30:46,070 --> 00:30:50,970 let's say response.locals.alert 533 00:30:52,830 --> 00:30:53,780 will be 534 00:30:56,910 --> 00:30:57,970 your booking 535 00:30:59,850 --> 00:31:01,023 was successful, 536 00:31:03,790 --> 00:31:06,883 please check your email for a confirmation. 537 00:31:10,330 --> 00:31:13,090 And we should also add some other phrase, 538 00:31:13,090 --> 00:31:17,960 which is this one, if your booking doesn't, 539 00:31:24,070 --> 00:31:27,743 select this, doesn't show up here immediately, 540 00:31:33,270 --> 00:31:34,523 please come back later. 541 00:31:36,140 --> 00:31:37,230 And this last part 542 00:31:37,230 --> 00:31:39,920 is because Stripe does very specifically say 543 00:31:39,920 --> 00:31:43,620 in their documentation that sometimes the webhook is called 544 00:31:43,620 --> 00:31:46,880 a little bit after the success URL is called. 545 00:31:46,880 --> 00:31:49,810 In that case, that success URL would then show 546 00:31:49,810 --> 00:31:52,677 all of the current tours, but only after that, 547 00:31:52,677 --> 00:31:54,300 the webhook would be called 548 00:31:54,300 --> 00:31:57,270 and the tour would be created in our database. 549 00:31:57,270 --> 00:32:00,040 Therefore, the new booking would not show up right away 550 00:32:00,040 --> 00:32:01,953 on the My Bookings page. 551 00:32:02,850 --> 00:32:06,220 But of course, everything still worked well in that case. 552 00:32:06,220 --> 00:32:09,583 And so, I simply reload, but later we'll fix that problem. 553 00:32:12,340 --> 00:32:15,080 Now we just need to call the next middleware. 554 00:32:15,080 --> 00:32:17,160 And that's actually it. 555 00:32:17,160 --> 00:32:21,390 Again, we only did this here for alert equal to booking, 556 00:32:21,390 --> 00:32:24,090 but we could now use this all over the place 557 00:32:24,090 --> 00:32:27,070 in our website by setting different alert keywords 558 00:32:27,070 --> 00:32:28,982 and query strings. 559 00:32:28,982 --> 00:32:33,982 With this, we put this message here onto res.locals.alert. 560 00:32:35,600 --> 00:32:38,940 Again, our base template will then pick that up 561 00:32:38,940 --> 00:32:42,320 and display it here into this data alert property. 562 00:32:42,320 --> 00:32:46,440 And so, all that is left to do now is to go to our index.js 563 00:32:46,440 --> 00:32:49,890 and read the alert from here and then display it. 564 00:32:49,890 --> 00:32:52,100 And so, that should be fairly easy. 565 00:32:52,100 --> 00:32:56,230 Here in public, let's actually do it right in the index. 566 00:32:56,230 --> 00:33:00,260 And the first thing is that we actually need to import 567 00:33:00,260 --> 00:33:01,343 the alerts function. 568 00:33:06,480 --> 00:33:08,160 That's not an app. 569 00:33:08,160 --> 00:33:09,343 It's here in index. 570 00:33:10,920 --> 00:33:12,090 Okay. 571 00:33:12,090 --> 00:33:15,883 And then down here, let's basically read that alert. 572 00:33:17,290 --> 00:33:22,133 So, const alertMessage, let's say, 573 00:33:23,250 --> 00:33:25,320 is document.querySelector, 574 00:33:28,742 --> 00:33:31,327 then the body element, dot dataset.alert. 575 00:33:35,350 --> 00:33:37,673 And so, only if there is an alert, of course, 576 00:33:39,760 --> 00:33:42,020 then show the alert 577 00:33:43,160 --> 00:33:44,250 with success 578 00:33:45,840 --> 00:33:48,000 and the alert message. 579 00:33:48,000 --> 00:33:50,640 And now this one small thing that I want to do 580 00:33:50,640 --> 00:33:54,630 is to change a little bit this showAlert function here 581 00:33:54,630 --> 00:33:57,210 because we actually have a lot of text now. 582 00:33:57,210 --> 00:33:59,780 And the standard time that the alert is shown 583 00:33:59,780 --> 00:34:03,163 would not be enough to actually read all the text. 584 00:34:04,210 --> 00:34:06,880 So, you see here that after five seconds, 585 00:34:06,880 --> 00:34:08,373 the alert is hidden. 586 00:34:10,126 --> 00:34:11,760 Let's actually allow the user to specify 587 00:34:11,760 --> 00:34:14,253 the amount of seconds that the alert is shown. 588 00:34:16,810 --> 00:34:20,320 We will do that as a default of five seconds. 589 00:34:20,320 --> 00:34:24,810 Here, we then simply do time times 1,000 590 00:34:24,810 --> 00:34:26,483 to convert it to milliseconds. 591 00:34:27,976 --> 00:34:30,690 Like this, all the functions will work everywhere 592 00:34:30,690 --> 00:34:32,270 with five seconds. 593 00:34:32,270 --> 00:34:34,790 Let's actually make it seven seconds 594 00:34:34,790 --> 00:34:36,600 if we don't specify anything. 595 00:34:36,600 --> 00:34:39,980 But if we want, we can then override this seven. 596 00:34:39,980 --> 00:34:42,040 And so, I will that now here 597 00:34:42,040 --> 00:34:45,370 and actually put it 20 seconds on the screen. 598 00:34:45,370 --> 00:34:46,203 All right. 599 00:34:47,360 --> 00:34:49,239 I think that should be it. 600 00:34:49,239 --> 00:34:51,060 I hope that made sense. 601 00:34:51,060 --> 00:34:53,993 Let's now just very quickly compile our bundle. 602 00:34:55,360 --> 00:35:00,343 That's npm run build, then tap autocomplete. 603 00:35:03,480 --> 00:35:05,990 That takes a little bit of time as well. 604 00:35:05,990 --> 00:35:07,373 But now it's done. 605 00:35:12,030 --> 00:35:14,340 Let's now deploy it one last time 606 00:35:15,580 --> 00:35:17,083 hoping that it works actually. 607 00:35:18,250 --> 00:35:19,083 So, git commit. 608 00:35:25,840 --> 00:35:27,513 So, Stripe messages. 609 00:35:29,670 --> 00:35:34,670 And one last time, git push heroku master. 610 00:35:37,451 --> 00:35:41,403 Let's now test it by buying yet another tour here. 611 00:35:42,830 --> 00:35:44,963 Let's get the City Wanderer this time. 612 00:35:46,490 --> 00:35:49,683 Oh I just see that there is a message already here. 613 00:35:50,810 --> 00:35:51,783 That's not good. 614 00:35:54,530 --> 00:35:58,500 And you see that it disappeared after 20 seconds. 615 00:35:58,500 --> 00:36:00,240 So, it seems like now, by default, 616 00:36:00,240 --> 00:36:02,993 it will always put this alert class here. 617 00:36:06,028 --> 00:36:06,861 (laughs) 618 00:36:06,861 --> 00:36:07,694 Yeah. 619 00:36:07,694 --> 00:36:09,990 That's because here it should be alertMessage 620 00:36:09,990 --> 00:36:11,063 and not just alert. 621 00:36:12,810 --> 00:36:16,800 But anyway, let's now just test 622 00:36:16,800 --> 00:36:20,433 if the message actually is correct when we book the tour. 623 00:36:24,410 --> 00:36:25,243 Okay. 624 00:36:32,470 --> 00:36:34,880 Now let's wait for it. 625 00:36:34,880 --> 00:36:36,330 Here we go. 626 00:36:36,330 --> 00:36:39,163 Indeed, there is our message. 627 00:36:40,130 --> 00:36:41,460 So, beautiful. 628 00:36:41,460 --> 00:36:44,420 Also, our tour shows up here. 629 00:36:44,420 --> 00:36:48,510 And you see that it really stays here for a lot of time. 630 00:36:48,510 --> 00:36:49,853 So, that also works. 631 00:36:51,532 --> 00:36:52,832 Let's just very quickly... 632 00:36:55,840 --> 00:36:59,383 And first, we actually need to rebuild the bundle here. 633 00:37:03,877 --> 00:37:07,170 Then we can add everything to our staging area, 634 00:37:13,580 --> 00:37:18,490 Message alert bug fix. 635 00:37:18,490 --> 00:37:19,670 So, these are some (laughs) 636 00:37:19,670 --> 00:37:23,500 really professional-sounding messages here already. 637 00:37:23,500 --> 00:37:26,313 Now one final push to Heroku. 638 00:37:29,670 --> 00:37:32,580 Now when we load our page, 639 00:37:32,580 --> 00:37:34,740 we should see no alert message. 640 00:37:34,740 --> 00:37:37,250 And indeed, now everything is clean. 641 00:37:37,250 --> 00:37:40,470 And so, I can now say that at least for now, 642 00:37:40,470 --> 00:37:42,977 this project is really finished. 643 00:37:42,977 --> 00:37:46,490 Once more, great job, congratulations, 644 00:37:46,490 --> 00:37:51,100 and well done for probably being one of the few people 645 00:37:51,100 --> 00:37:54,350 who actually are making it all the way to the end 646 00:37:54,350 --> 00:37:58,370 of the project and really building this beautiful website 647 00:37:58,370 --> 00:38:01,780 and also API that you can now put on your portfolio 648 00:38:01,780 --> 00:38:02,923 and show the world. 49757

Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.