Refunding cancelled reservations - clearbnb - Part 12

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up welcome back this is episode 12 of clearbnb in this episode we're going to mark our reservations that were just booked using the webhook event that comes in from stripe for checkout session completed and we will be marking the reservations as booked so that we can keep track of when something is paid for and then we also if we have time we might make it so that when you cancel a reservation it refunds the guest but we are yeah we're gonna play around and see how far we get so um in the last episode we talked about setting up our web hook handler so we've got web hook handling set up we've got requests coming in from different sources we're creating an event object storing that in the database and that represents the inbound event that we're ingesting from any webhook source and now we have this processing tool where we have a background job that is going to performed based on that event and then we're going to switch on the event source so in this case we're going to look at stripe and if it is a stripe object or if it is a stripe event we are going to call handle stripe passing that event down into handle stripe and inside of handle stripe right now we are just looking at one event type that is check out session completed and what we're going to do is look up the reservation so we're going to say something like reservation is reservation dot find by session id checkout session.id and then if um reservation.nil then we want to raise some exception then like uh raise like no reservation found with that with checkout session id equal to um check out session.id and then we need to make this a double quote because we're using string interpolation okay so if there's no if the reservation is nil meaning we didn't find it here i'm going to raise this exception which is going to be handled above and rescued and we're going to update our underlying event object with that message and it should mark the status as failed otherwise if we do find a reservation then we want to update it so we want to say reservation reservation.update and we want to set the status to booked so that's going to mark the the status as booked if we wanted to we could also track the payment intent id now which yeah let's do that so rails g migration add payment id add well i guess stripe stripe payment intent id to reservations uh stripe payment intent id it's a string the payment intent id like the payment intent is the stripe object that actually moves money and that's going to be the id we want to use when we're refunding the reservation later so we do want to store this somewhere and in this case we're just going to store it directly on the reservation itself and yeah so when we receive when we receive the web hook notification telling us the checkout session is completed the payment should be successful at that point and the checkout session will now have an associated payment intent id and so we want to associate that also inside of our event job um with the reservation so here we can say um stripe payment intent id is going to be checkout session dot something i don't know what is it called so if we go over to the developers tab go to our events look for the checkout session completed event and that's what this is check out session completed where in the world is the payment intent uh okay it's at the top level and it's just called payment intent so we should be able to say dot payment intent okay and hopefully that updates the reservation as expected to be booked actually this is item potent if we receive checkout session completed twice with the same event data process it twice we should end up with the same updated event and it should be yeah so this should be item potent meaning no matter how many times we handle the same event we're going to end up with a booked reservation and that's fine all right the next step here is we want to test it out so in um in the previous episode where we went through background jobs i set up a webhook forwarder and listener so that we can wait for webhook events to come in and we're forwarding those to our localhost 3000 web hooks stripe and so any event that fires on our stripe account will be forwarded to that url so what we want to do now is go back to our our listings so for the listings home page here let's find listing three listing.find three dot touch we're touching the listing because there's an after commit hook that will go out to stripe and create stripe product id if we've never created one yet for that listing now we can click on reserve and when we hit reserve we're redirected to stripe checkout and we can pay and after we've paid then we're redirected back to to our rails application to the success url but we should have received some web hook events specifically checkout session completed and we should have processed that event here um so we've got our webhook controller is handling a create it's in cueing the event job to be performed later and that event job if everything go or if everything went well we should be able to say like reservation.last and the status should be booked booked boom look at that status is booked we have a stripe payment intent id we've got a stripe checkout session id this is really good this is this is great this is really really great so now we have a full reservation system where we can have a guest book property it flows through the entire system and we come back and we have a paid reservation right now this is just like the default reservations show page so why don't we update that just a hair reservation show and text 2xl for margin bottom four and font bold and then we'll just say like um reserve or like um so the reservation should belong to a listing and it should have a title okay good and then here what we want to do is just print out like inside of a pre-tag maybe we can do uh at reservation dot to json and we need to have a show that has at reservation is current user.reservations.find params id so that no one can see anyone else's reservations and here is our reservation and i thought how do we make it pretty how do we make that json pretty listing show pretty generate okay uh okay so now we've got the checkout session we've got the payment intent id etc etc etc what's really cool is if we go back to our stripe dashboard we can go into home and we can see our payments and we have these several payments for the different uh bookings that we've received so this one here 260 bucks and that includes the 200 nightly fee and the 60 cleaning fee now uh at this point we are just creating reservations that don't have any concept of time they are just like a booking so you go to a listing you find the listing that you want you because they don't listing.all.each do l l [Music] l.touch this is going to cause a bunch of api requests but whatever i'm annoyed by having to come back in and deal with it so this is creating stripe products for every single listing which is like technically what we want because inside of our product catalog we should have every single listing should have its own product and then every time we get a booking we're going to create a price ad hoc for that product and that'll allow us to like associate how many bookings are for a specific product and do some nice reporting and things like that but it's still going whatever i'm i'm guessing that this one is already done so i'm just going to refresh click on reserve and we're redirected now we can pay 200 a night if we want to book this property and again we can put in the test card jenny rosen blah blah blah pay and that is great we're redirected to reservation we see that it is here and note that it's pending still but that's because we probably haven't processed the web hook yet if we refresh the page it still hasn't processed yet and it still hasn't processed yet and did we forward it oh you know what i bet i bet our stuff is just behind because we're like creating so many products like every time we hit those products oh yeah here we go checkout session completed just fired so this should update now no what's up what why are you not working uh let's see did this get a this should have gotten a web hook should have gotten hit by a web hook is this the web hook yes okay so this is good because what we can do is we can find the event real c um yeah event.last event account 220 events holy moly okay so yeah so event dot i don't know why okay let's go look at our web hooks controller perform later that should have worked event dot let's see there's 220 so event dot all that each do e event job. perform later e that should go through and schedule all of them to be performed again okay now we're booked uh not sure yeah so i don't know what the delay was but that seems to work okay uh it might have been the stripe cli wasn't forwarding it or no because we wouldn't have even gotten it so i don't know what's up or why that didn't process but it didn't and here we are but now it's processed and we see that it's booked and we've got this payment intent so that's cool um why don't we add a button yeah let's add a button to this reservation show page which allows us to cancel the reservation so reservation show and we will add a huge button here that's like um observations actually that we'll do the whole reservation path uh i think we actually want to cancel we want to have like a cancel path cancel reservation path for at reservation and we'll make the button say cancel and now we should have a route where we call this do and this is going to be not for all reservations but for a specific one or a member of the reservations collection so we say member do and this is going to be a post request to slash cancel and this will go to um reservations cancel reservations controller def cancel and here we want to like look up the reservation and we want to call cancel on the reservation which should ultimately go to stripe and refund the payment intent related to the reservation so i think technically like this creating the checkout session business and um canceling like sending the api call to cancel it's it's mushy where it should belong like should it belong in the model should it belong on the controller should it belong in some other class or some other object who knows but for now i'm just trying to keep it simple and readable and easy to follow without jumping around a ton so i'm just going to plop it right here and uh yeah hopefully this works fine so what we want to do is we want to say we want to i think what we want to do is cancel oh we want to make a refund on the payment and we want to then listen for the refund being successful in the web hook and in use the web hook as the the notification to mark it as cancelled because we don't want a user to cancel and then it not actually refund their payment and then then be sad that like it shows is canceled but they didn't actually get their money back or whatever so what i want to do here is like make api call to refund the payment and so i forget how to do this refunds so the refund object we're going to create a refund and we're going to pass in not the charge but the payment intent so here we're going to say refund equals this and the payment intent is going to be at reservation.payment stripe payment intent id um and yep that's right okay uh that should refund the payment and that's great and then we'll redirect back or redirect to um reservation show or reservation path for at reservation um and gosh you know what we could have like a lot of statuses like oh it's in the middle of refunding like it's in the process of refunding and then you can mark it as like refunded and we probably want to store this stripe refund id so let's do that rails g migration add stripe refund id to reservations um stripe refund id is a string because we want to be able to like look up that reservation later by its i buy the refund id when we receive the refund notification so back to the reservations controller so after we make the refund through the api we want to say at reservation.update stripe refund id is refund.id okay so that should be fine i do think we want to add a state here that's like refunding or like yeah refunding i don't know or canceling i don't know whatever like let's it's yeah that's fine um okay so this is going to create the refund for that payment intent redirect back to the reservation path which is going to show as booked still but whatever um and then what we want to do is no yeah we gotta like yeah we gotta we have to have like some sort of like pending thing like um canceling is three sure yeah whatever cancelling and we'll update the status to canceling okay so in terms of events we should receive a list or like we should receive an event for refund what okay let's just look at what happens when we when we actually do it so reservation six rails db migrate okay we've got a cancel button here we're going to click uh yeah let's yeah whatever we should make it clear like cancel booking and this is going to like try to cancel your booking okay so here we have the refund id so now if we go back over to our oops to our dashboard and we go to the developers and we go to the logs we should see our events we should see refund for blah so charge.refunded what is the next event here that's it charge.refunded does this have the payment intent oh it does okay cool so inside of our webhook handler our event job here we want to add when charge.refunded we want to look up the does this have the refund on it pqr [Music] refunded true uh what how does it not have the refund id oh refunds there's like several and it should just have one because we're doing full refunds only we're not doing partial refunds so okay so the charge is event.data.object and the reservation should be reservation dot find by stripe payment intent id and this will be like charge.payment intent and then if the reservation's nil no reservation found with payment intent id this thing charge.payment intent otherwise now we're going to say reservation dot update status cancelled and that's it so razer i can't remember if i used 2l's 2ls cancelled is a weird spelling okay so what we want to do is actually just retry this event and see if we can get it to oh you know what it's already it's already handled right okay it was already sent to us event.last but and it was processed so it's showing processed here so what i can do is say event.last e equals event.last e dot update status is pending i think and then i can say event job.perform later e and that should reprocess the same event and this time it should go through this flow and mark the reservation as cancelled so before we run that if we say reservation.last we should see that it is in the canceling state it's not cancelled and then we want to say yeah event job.perform later e nope per form later okay and then if we look at the reservation.last now it says cancelled okay so that flow should work now if we go through the entire process this says cancelled now we should probably remove the cancel button and a few other things and make it like nicer but whatever all right so let's come to aqua some qui reserve we're redirected to stripe checkout we pay test okay we click pay we're redirected back to the reservation page it says pending we should be able to refresh okay i think oh there we go there we go now it's booked and we've got a payment intent id so that's successful and if we wanted to now we should be able to say cancel canceling so it's in the middle of canceling it has a refund id and then if we refresh again canceled boom so that is the whole flow for booking now we need to like attach all the other stuff to it like when are they checking in when are they checking out like what's the how many guests are coming how many adults how many kids how many whatever and we need some messaging and blah blah blah but at least now payment is flowing to the platform um we could just like like cut off some of the money right now and say like okay we're just going to take a five percent cut of every single reservation three percent two percent one percent whatever or um and we can do that as part of stripe checkout with connect or separately we can use stripe connect where the hosts sign up and we wait until check-in to actually pay out the host and this is what airbnb does and it gives them a ton of float where between when someone books and when they actually check in airbnb is able to have that money that they can invest in whatever instead of just paying you out as soon as a guest books um it gives them a lot of opportunity to use those use that capital for their own investments which i think would be good for clearbnb as long as we're clear about it and transparent about where the money's going et cetera et cetera et cetera so at this point i think we're good with the whole flow we can make bookings we can cancel bookings and refund them and yeah i guess the next part of the process will be um i don't know but we'll we'll get to it we've got a plan here for notion i'm pretty burnt for today but i am super excited to uh to jump back into this and keep going with clear b next time so thank you so much for watching i appreciate your time and attention if this was useful to you would love a thumbs up and uh yes maybe subscribe if you love this type of content i am trying to make as much as possible just to help walk through my thought process of building out different features in rails um we're going at it a bit of a hacky way just kind of like plowing through and adding as much as we can but i'm trying to do my best to like mention and call out best practices where where applicable so if you know there's better ways to do things please in the comments let us know and we'll try to update and we're all here learning so all right see you next time [Music]
Info
Channel: CJ Avilla
Views: 89
Rating: undefined out of 5
Keywords: cjav_dev, web development tutorials, web development for beginners, vim, ruby, rails, javascript
Id: nto4vXDqMOg
Channel Id: undefined
Length: 25min 13sec (1513 seconds)
Published: Wed Dec 08 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.