2.4 Payments and Receipts
The next part of the dining experience is to pay for the meal. We'll implement an endpoint that supports the ability to pay for an order.
1.Introduction3 lessons, 12:00
2.Building the API10 lessons, 1:53:48
3.Conclusion1 lesson, 01:08
2.4 Payments and Receipts
In this lesson, we'll focus our attention on pain for the order. So the end result of this operation is a receipt record, which will hold some data related to the order and also around the payment method for example. The way we're going to do it is by adding yet another method in the OrdersController that will generate a receipt and handle all of the logic behind a payment process. Pay attention, though, as we are not really going to process money transactions or anything. This is just gonna be simulated. We're not gonna handle credit cards or anything, so don't worry about that. So let me create that method right away. I've collapsed everything else so we can focus on this very method, which I'm gonna call Pay. Pay is gonna pick up on an order. So we'll definitely need to fetch that first. Let's do params :id, just like you see. And then we're gonna need to compare the orders total amount to be paid based off the items. And also, we're gonna have to create a receipt if the amount that's being sent for payment matches the total amount. So we're gonna start by telling our story. We want to make sure that the total amount for that order matches a value that we'll have. Let's say for example that we want to pass an amount parameter like so. So if the order's total amount matches what we are sending to the clerk that's handling the payment, then we are going to generate a new receipt. The receipt is going to be a new instance of receipt, just like that. We're going to refer to the order like so. And then also, we might wanna pass in some other kinds of information. I'm thinking a payment_method, for example. We'll send in the payment_method as you see just like so. Let's say the customer want's to pay either with cash or a standard card, you can pass it in like so. Remember, this is my version of the story, most likely, you would like to find some other things that would bring value to the applications. For now, this is the only thing that I came up with. So, let's see. Let's call @receipt.save. And then render that receipt as JSON, like so. We'll type in 204, meaning that this payment process was successful. 204 stands for no content. Now if the receipt is saved, then we are going to render it. Otherwise we're going to render some errors. So let me just copy this. And instead I am going to render the errors like so. And the status code should be 422. So it seems that the story is told at last. We'll retrieve the order, counter the .total_amount to the amount that's being sent, and if it matches, then we're going to send a new receipt. Or basically, just create it and render the receipt's data as a JSON file. Now, what about this .total_amount method? This is going to be tricky because it's not just a method. Let's start by going to the Order model. And here we'll create a total_amount method. There's really no big magic here, just a standard method. From here we're gonna go to the items and we're going to invoke the inject method on 0 and then a block. Block will be inserted right next. So let's see. Let's create a block that takes an item and the injected 0. So the, sum, as it is. Or maybe it's the other way around. Let's see. sum and then the item and for each one, we'll add and the sum + item.price. Okay, so the total amount will be the sum of all of the items' prices. Well wait a second, we don't have this price attribute anywhere. Well, the only thing left to do is to generate a migration, which will add that price to the items table. We'll use prize as an integer, like so. Okay, there you go. Next thing is we add the migrate step. Before I do this I'm actually gonna go to the migrate step. Let's see, let's open the file and make sure I provide a default. Let's say $2. So the default will be 2. Everything from the soup that we've created, the pudding, the steak, and the fish and chips, all of them will cost $2. Now, I can go ahead and migrate the changes. And remember that the only reason that I'm typing this default value is so I don't have to type it all the way manually, okay? If you were to do it manually, like so, you would have to type in the console command. And for each item, so I'm just gonna select Item.all and retrieving the array by using to_a, you look at the whole list of items. For each one of them you would have to type in a specific price. For the sake of simplicity, I've just added a default value. That's it. Now let's go back to where we were. Let's go to the Order again, and this time we'll be able to inject each item's price to the sum, and this will be our total amount. We're gonna compare it to an amount variable being sent from the request and if the total amount matches the value there will create a new receipt. The receipt will refer to the order, and also for example, a payment method. And you could add more stuff in here. Maybe you wanted to take change into account, and thus you would have to change this, for example, to And you would calculate the amount of change that would need to be prepared for the customer. And more stuff I'm sure. For now we're gonna stick basic example of the amounts matching exactly. Now it's time we pick up on the curl script we have here and just create one more function. We want to pay for an order. So pay_order, this is the name of the function, and then we create yet another curl request. The command will resort to a post route with some JSON, which will send, for example let's see, pay_order.json. And then what we want to do is to pay for that specific order. Let's now take the chance to add it that pay_order.json file. We'll create that file right away. And the JSOn that's going to be part of it, is something like this. Specify the amount. In this case, let's see. I don't even know what the total amount should be. Actually, let's do something. Let's just forge some value here. Actually, 3.5 looks just fine. Next we're also going to set a payment_method, which can be something like cash. Let's just simulate that behavior for a second. So the hashes right here, we don't have any complicated stuff, just these two values. Now, let me just fix something right away, and that is the else condition for not matching the amount. We're gonna render a JSON, which is actually going to render a hash directly. I'm going to say that we have a message key and the respective value will be something like, You didn't pay for the exact amount. And that's it. Also let me just make sure that I type in the 422 status code. And for now I think we can just start by typing bin/curl, and then pay_order. Let's press Enter right away. You will see that, well, we have an error. Let's take a look at the logs and make sure that we know what we are handling. Of course we don't have that route established yet, so we're gonna go to our editor. Edit the routes file and create one more route in here, so we use pay. The pay route will match the Pay method in the OrdersController. With this, I'm gonna go straight to the same command. And this time, you can see that we didn't pay for the exact amount. Let me do something different. I'm gonna go back to the OrdersController and I'm actually going to specify the amount. So I'm going to directly inject the orders total amount. This is going to be actually funny because since I don't know the total amount for that order, I will forge our script in order to force the receipt object to be created. So let's see. It seems that we have exactly one item in our order because the total amount is 2. For that I'm gonna go back to our script, let's see where it is. There you go. Now, I'm gonna change the actual pay_order.json file, and change this to dollars. Now, if I run the script right away it won't work, because if we check the logs you will see that we don't have that Receipt model. That's easy to fix because the only thing we need to do is to generate yet another model called receipt. The receipt will refer to an order and also it will have a payment method which will be a string. I think it's okay. So, just generate the model. There you go. Migrate the database. There you go. And now we can try and run this command again. When we do, we will see 204 as the status code, which has No Content, and indeed we don't have that content. But if we want to have the content being displayed, then we need to change the OrdersController to be instead of 204, 201. Let's try and do it again. I'm gonna create a pay_order like so, and this time you will have the entire object. So there you go. Now we've handled payments. Obviously this is a very rudimentary approach to payments, but still, the business logic is there. Of course you would like to have some sort of payment mechanism or gateway integration, stuff like that.