 at the talk of more money, more problems with Ruby. This is not Tender's Love Talk, that's somewhere else. My name is Federico, you can find me at Twitter, at Fedesoria. I'm a Ruby developer from Mexico. I know there's some from here, I come from Monterey and so on. I've been doing Ruby for about eight, nine years. This is my sixth Ruby talk, Ruby conference. But as I say, long time listener, first time caller. So, first talk I gave here. I work at Peva Group in San Francisco, I'm the CTO. And the reason why I'm giving this talk is relevant to the startup that I work on. Peva Group for the past three years. Let me give you a little bit of background of what we do. Basically we're a checkout option. So as you're going through a shopping cart or something, you'll see this button that says split the cost with Peva Group. You click on it, put down the emails, and it splits the bill. Although we do sort of like a Kickstarter model, so at the time where the entire group gets together, then we charge all the cards. So as you can see that brings sort of a complexity, a little bit more of complexity that you usually get when you do regular transactions because we have to do, for example, we had groups of 200 people. So we have to do 200 transactions in terms of milliseconds at the same time and what happens when you have car failures and all that. So it's very relevant. So again, one more problem with Ruby. You might have been expecting something a little bit like this, but I'm actually gonna pull a buff daddy to PDD thing. So we're gonna change the talk to do's and dons with money. So to get started, let's talk about money and databases. One of the, a lot of people probably know about this already, but there's this big issue when you try to do this. I remember talking to a couple of developers, it's like, oh, it's because JavaScript sucks. That's why you're gonna get an error. I actually know, that's pretty much every language. The problem is not inherited from the language or even a framework or anything like that, but it's this thing called flow. It's very useful, but not for everything. As you might read from the definition of Wikipedia, the formulaic representation which approximates a real number. So it's to support trade-off between range and precision. Emphasis on approximates. So the problem with floats is, it's kind of inherited of how computers work. So everything gets stored in bits, right? First bit says, is this a negative or a positive number? A couple more bits say, what's the exponent and the rest of the bits will say the fraction. So following that notation, when you want to store one third in the database, it's actually impossible, but computers are smart for us. So they'll try to represent what one third is, but that's why you get this kind of problems in which you get this number. So don't use floats for money, please. This is a thing that you'll find out when you're starting your own startup or program that uses money. It's just, don't ever use it because you'll come up with like weird numbers at the end of all your operations. Do use integer, sorry, there's an inherited sort of cool thing about integers, which is you cannot store decimals on it, but it works really well with money because just store everything in cents. And then it does a lot of things for you because you cannot have 0.1 cent in your database. So it forces you to do the right thing. Also, most payment processors will ask you to pass everything in cents because it's almost, I'm actually surprised that it's still not sort of a rule. And the reason why it's not a rule is because we have this thing, big decimal. Has anyone used big decimal before in Ruby? Okay, it's awesome because basically what it does, it's in the database in the background that if it's not a database that supports big decimal, it just stores everything as an integer. And when you fetch it, it will convert it into a big decimal sort of like a float or a double. And then you can play around it and when you save it, it saves it back to an integer. The only problem is that there's performance issue with this, so if you're getting started and you're playing not a lot with big decimal, it's fine, but as you scale up, all of a sudden you're gonna get a call in which says like, oh, your server's now need 10 gigs of RAM, why? Because you have big decimals there. So that's why you should probably stay with integers and that you won't get any performance issues going down the road. We have another one which is rounding. When you have money and you have 0.5 cents, right? Where does it go? Does it go down? Does it go up? Everyone knows what round-up and round-down is. Do people know about rounding a lot of like, besides those two rounding? How many of you think rounding methods are there? It's like just shout out a number, you know? Anybody? Well, I'll give you a hint. Almost infinite. There's so many, there's papers being published almost every two weeks about different types of rounding. There's like, so just, you'll go crazy if you go just on rounding, but for now, we're just gonna focus on the one that is mostly relevant for money, which is round half to even. Basically what it says is, sorry, okay, round half to even. Also known as bankers rounding. The reason it's called that is because, well, all banks use it now. Because it's the fairest of all the roundings. So, given a reasonable distribution of Y, which is like number of roundings, it will be fairly the same amount of money that you will be collecting at the end of like X number of roundings. The way it works is it goes to the, when you round, it just goes through the even number. So you don't care if it's to zero, to one, two, it's actually, sometimes it will go down, sometimes it will go up. So, 0.5 cents, it will go to zero, but if you have something like this, it will go to two. Cool thing, Ruby actually supports this. It's called round half even. You can pull it from big decimal and just call it the mode. Notice I'm using big decimal here, but it's for reasonable purposes. For explaining this, because the code to actually bake it in into your own operations, let's kind of be bigger, but it's up to everyone to do this. Also round half even, since we're Ruby developers, we can also call it banker, which is a nice thing that was added to Ruby's tenor library. The other tip would be to not mix integers with floats. Remember, Ruby, everything's an object. So, when you try to do things like this, Ruby will not complain at all, which is nice if you're doing non-money operations, but you can see what will happen if you start mixing these things. Next. One of the questions that we get asked a lot in the currency environment, it's do you mix dollars with yen? So, when you charge someone for a transaction, let's say your system is here in the US and then someone wants to pay from China and it's like, hey, I want to purchase this thing. So, now you're bound with two decisions. Do you take the currency, the foreign currency, or you change that currency into US dollars? From my experience, keep it as yen. Don't change it to dollars. It involves so much complexity and someone will end up losing money in the transaction and then keep in mind expenses and how you need to grow with this. It's crazy, especially since foreign currencies just go up and down more lately than ever. Even big sites like Expedia, if you try to buy something with, let's say, Pesos, you actually have to go to a different Expedia site that everything's in Pesos. They don't even mix it in their own website. You have to go to a completely different server. So, don't do that. But do save the current exchange rate. And I'll show you a little bit more later about this, but since you're storing Pesos, Gens, and everything in their main currency, you want to know what the exchange rate is. Because later on, when you're, you know, someone asks you, like, what are your financials? How much are you earning? It's like, well, yen from eight months ago, well, that would mean, yeah, no clue. It might be totally different from what it is right now. Just store in the background so you know for your own analytics how much money you're making. But this time, you're probably like, well, isn't this supposed to be easy? You know, we're Ruby developers. We don't have to be hacking on the keyboard and just, you know, keeping all those integers throughout the system and everything. That's my sound, by the way. How are we gonna do this? There has to be an easier way to do this, right? Boom. Well, if everything's an object, let's make money an object, right? And then just put everything in there, all your methods and everything, so we make sure that money behaves like an actual object, which it is. Or, also, keeping with the Ruby developer thing, install, well, Jim, right? Jim, install money. This is pretty cool. It helps you a lot with sort of everything I just talked about. It's baked in. So it really helps you out avoiding common pitfalls from the very start. To give you an example, you require money. In this case, we're gonna use Google Converter to change the currency, but it supports many others. And it's as easy as, you know, one is a new, again, integer, takes an integer. You pass US dollars and then you have the object. And then let's say you wanted to change this to currency to pesos, which is MXN. You just set up the full bank and then you exchange it to pesos. But, again, since it's an object, it helps us do this kind of things. So, for example, if you want to multiply something for the money object or float, you know it's gonna be a bad thing, right? So you can just patch this and say like, well, if you're trying to multiply this by float, racing is captioning. So, I mean, this is a good thing. Again, Ruby world, everything's an object. Another dude, my great job is your friend. I don't even notice in the recent years, but there's a thing called PCI compliance. So if you're taking money, you should be somewhat aware of this. Not saying that you have to fill the questionnaire. She's around a thousand questions and it takes about three to four weeks to finish. So, one of the reasons why a lot of people do not actually know about this is because now the way we take money, it's done in the front end and through an iFrame or stuff like that. So, credit cards and all this doesn't actually ever touch your back end. So you never have to worry about PCI compliance. But once you do that, which is the, it's called like transfer of redirect when you have a form that sends a request to another domain and then you bring back a token. And then with the token, now you can actually do transactions. Do not do the transactions on the web request ever. Because there's an inherited problem with the web, it drops requests at any given time. So you lost the money, you keep the token, but it's lost. So just keeping from, taking from Rails, you just use ActiveJob, I use ActiveJob here just because it plays around with everything. But you can use SideKick, you can use many other options. We actually use SideKick, so I'll stop there. But yeah, so run the code for the payment in the background. This way you can actually have many other things that will help you in the background to do certain caveats that have to do with payments, such as this. Don't do this. So I'm using threads here simulating requests. Basically, this will be a problem because the way we sort of request or threads in this case, if you capture a dollar and you wanna pay out to Sam that dollar, this could happen at a different times. Or even if you have an error on the capture, there will still be a payout for a dollar. So this is a very short way to lose money. So keep this in mind. The way we solve this is through atomic operations. This is a very, very fancy word and for this things, which is either using ActiveRecord transactions or mutex operations. The way we use transactions, which is the easy way to do it, will be just called dot transaction do. And then if the capture fails, then it won't do the payout or even better. If the payout fails, then it will try to roll back the capture. Obviously, it's on your own to actually create the rollback, but there's already methods that help you deal with this. I can't stress how important this is. For example, in our business and payback group, we do this, imagine there's 200 captures for one payout. So we definitely cannot pay out until those 200 captures go through. So this is a thing that almost never, I think out of rate right now, it's like 90% of all payback groups fail on the first try because 200 cards, one will fail. There's always one failure. So this is very important, otherwise we'll be paying out like crazy. But this is RubyConf, so Rails. Let's try this in Ruby. So we use mutex. Who has ever used mutex before? Okay, we've got more hands than I thought I would have gotten. This is awesome. So we call it synchronize, and then once we do the capture and then do the payout, everything will be synchronized. So what mutex helps us with synchronize is if this were to run, let's say three times, total capture would be in sync. So instead of having this two thing, like running in parallel, total capture will always be 300. Doesn't matter if it's running in parallel. That's what synchronize does. It basically does a lock on the variable and then forks it. It's pretty cool. I know it's just a slide, but this is like super heavy Ruby threats and semaphores and everything. It should really look more into this because it has a lot of uses even outside of dealing with money. Another do would be to be mindful of timestamps. Timestamps are super awesome. If you're using Rails, you know it's like baked in. You have timestamps everywhere. And when you're dealing with money, it's very important to know when money's coming in now, especially if you have troubleshooter going to support. But one of the problems that come down the road, actually, when you have a problem, you realize this. But timestamps are not always in sync with all your services. Database might be in a different time zone that your server, then your gateway provider, then your computer or whatever. So be mindful of timestamps that have to be all in sync or at least we try to have them in sync. This would definitely hurt you in the long run. There are things to keep a ledger. If you deal with money, you gotta remember one of the things that we do with programming is just bring problems that we do in daily life and try to solve them with computers. Keeping a ledger is the thing that you still have to do. Coming in, RRS will really come down and you don't do this. So what's the main thing about keeping a ledger? It has to be read-only. So your transactions, remember when I told you, it was like you do a transaction, model.transactionDo, and then you keep doing your transaction. If it happens, if everything goes well, then get stored. But if something goes wrong, it just will not be created. Because you don't want someone going back. It's like, oh, remember I charge you 20, let me change it to 10. This way it will avoid any of those errors that a developer might come in and try to update an attribute or something like that. Well, since we're dealing with money, I'm guessing this will be charging credit cards, number one use. But what happens when a user says you know what, I don't want to use your service, also I don't trust it anymore. Please delete the cards. Well, that's going to become an issue, right? What about deleting the gateway? That might be an issue. Right now, you might be using Stripe to capture all your payments and you're using credit cards, which is sweet. You're starting out first code, charging 10 dollars. That's awesome. But as you scale it up, you know what? PayPal gives me a better rate for break tree. And you have to swap them out. And it's a totally different API. It's very different how it works. You might not be using credit cards. It might be Devin, Bitcoin, PayPal, Dodge. Any Dodge fan, Dodge coin? Awesome. Yeah, so programming principle, just a couple. Really, start using more methods, more abstract classes, use gateway, payment source. And then later on, you can just not even remove one, but just add more. This is the difference between, and I talked to other developers at different companies, and this is the difference between, oh, Stripe just implemented Bitcoin, or it took us a year to remove 30% of our code and now be in abstract class and be able to implement Bitcoins. It's really gonna help you down the way. Besides, I mean, five years, we don't know where we're gonna be paying with. Apple pay everyone? I don't know. Fraud detection. First thing that you hear people talking about fraud detection is machine learning. There is a huge hype, and there should be. It's awesome, you learn a lot, but there's really, there's a starting point. You should never start with fraud detection with machine learning, otherwise that's gonna be the bulk of your entire company. There's small tips that you can play around with for this. Basically, sometimes it's just doing a grep from social media, Facebook. If you find a profile that might be an indicator of them being a real person, you can check IP addresses versus the origin of the contract card. I don't know if a lot of people know, but there's the bin card, the bin number, which is the first six digits. You can tell which bank is from which country. You can even tell in the United States you can tell for which state your credit card is from, like which bank that you enter in. So it's a very handy use, and a lot of the payment processors use this. It's a way to know if, like, oh, you're using a credit card from Omaha, but you're in India, that kind of doesn't sound right. You go to binlist.net, you can find everything. There's actually a gem for it, so it does the query automatically for you. Thank you. And also, we're hiring. So the question is issues between transaction and logs. Locks. Okay, no, so the lock would come in since we're using the mutex, that helps a lot. So first of all, you use the delay job in the background process. So as you're running through it and start capturing the cards, it's sort of, well, the behavior of what we do is a payback group will have, let's say, 100 cards. So the background process, it's actually for the 100 cards instead of each card on its own. And then as soon as you fired the mutex, then it won't be a problem because they're all being synchronized. So it may be not a problem for us, but it's something to be mindful, though, if you're start running multiple transactions, independent transactions. Okay, well, short and sweet. If you have any more questions, please see me out here. Thank you very much.