Alexandre Grégoire, of Transit, joined the PAYMENTSfn 2020 event providing us with a keynote presentation about facilitating payments across multiple modes of transit.
In his talk, Alexandre explains the lessons his team learned in the journey to enable customers to pay for urban mobility (bikeshare, scooter, public transit) all in one unified way. The session covers what it takes to integrate with payment APIs, how to scale across multiple integrations, and how to make the customer experience as seamless as possible.
Making Urban Mobility Payments Seamless
Want to learn more?
Download the Payments Orchestration Playbook from PYMNTS to learn how Transit reduces the payments friction found in highly fractured and localized transportation systems with Payments Orchestration to tokenize payment information and facilitate payments across services.
Rough Transcript of Transit: Making Urban Mobility Payments Seamless (edited for readability):
Hi everyone. First, I want to thank the folks at Spreedly for organizing this conference. It's my second time, I attended last year. I've had tons of great conversations in the hallways and even if we won't share hallways this year, I'm certain that it will be the same even in this new format. So, my presentation is named, "Making urban mobility payments seamless." I'm Alexandre Gregoire, or Al Gregoire. I've been working at Transit since 2016 as a software engineer. I head the partners integration team. It's a team of five developers working on integrating bike-share, ride hail, public transit ticketing, and all of those new things.
If you've never heard of Transit, we're an app that tells you how to get from A to B in your city without your own car. We're the app commuters used the most, after Google Maps and Waze in North America. We were founded in 2012 and we grew in the last couple of years to a team of about 60 engineers, designers, planners, marketers, and others that are passionate about making car ownership in cities obsolete.
We're based in Montreal, where we were founded. We're trying to dive in about 200 regions in the world and we are the official app in dozens of public transit agencies in North America. We also like to write a lot. We've been doing a bunch of interesting things around crowdsourcing, bus schedules, public transit, data compression, mapping and all of that. So, definitely check out our blog. There's tons of interesting stuff there. And these past weeks, we've been working a lot on understanding the challenges of public transit these days. So, we have a lot of data and insights to share about what's happening right now with coronavirus and public transit. So, you can check that out as well.
One thing that sets us apart from the other apps in the same field as us, is that we want to make it easy for you to plan and book a trip using a variety of modes. So, let's say you live and you work downtown. So, no worries. We've got bike-share for you. You live in the suburbs or far from the closest rail station. So, no worries again, we've got ride share, micro transit and even scooters to get you there. You are an experienced commuter or even an occasional one. So, we've got mobile ticketing for you. So, by the way, what you see on the screen is a bus wrap that was done by the MTA in Baltimore and on the right it's ads for public transit ticketing for Saint Catherine's in Ontario, in Canada. We've been doing aggregation of new mobility options for a while, but in 2016, we started dreaming of a easier way for users to sign up and use new services.
So, if you're in a large city and you want to take out, for example, a scooter for a ride, you have the choice to use one app for that scooter, another app for this scooter and even another app for that third scooter that you see there on the side of the road, or in the best scenario, you could use an app that will allow you to unlock 70% of the scooters in your city, 50% of the bike-shares and also let's say 26% of available cabs. And maybe somewhere in there buried, there's an option to find a bus schedule in there. So, that's not the world that we want to live in. We, and I don't want to download 15 apps to navigate my city. So, that's why we've been doing aggregation for a while and that's also why we created Transit accounts.
So, we thought of Transit accounts as a seamless, integrated way of using and paying for your fare in your city. So, right now you can use Transit accounts with bike-share systems, such as MoGo in Detroit, Bike Share Toronto in Toronto, WE-Cycle in Aspen, BIXI in Montreal and a couple more as well. You can also buy your bus fare in Denver, Las Vegas, Saint Catherine's transit, half a dozen Ohio agencies, Santa Monica and Cincinnati and also more to come. We're also hard at work on adding more modes. Previously, when you signed up for a new service in the app, you would have to enter a bunch of information. Let's say, to create your account, your name, date of birth, your payment information, your billing address and everything. And eventually you would be able to buy a pass, for example, this is for BIXI in Montreal, which is a local bike-sharing system.
Let's say that you travel to Detroit and then you want to take out a bike there from MoGo, which is the bike-share in Detroit. You'd be stuck to... You'd have to re-enter all of the same information. And this happens also if you want to sign up for another service in the same city, you still have to re-enter all of the same information, take out your credit card and everything and this was pretty depressing to us. So, we created Transit account to solve this problem. So, when you sign up for a service using Transit, we actually record the information that you enter with your consent so that it can be re-used later when you want to sign up to buy your bus pass or take out a bike later, or maybe a scooter or something like that.
So, we want a situation like the one that you see right now on the screen where on that second screen there, we ask for your consent to send that information to MoGo and then you accept their terms and you're in business. Everything's good. We've been working on this project for a long time and we launched it a bit more than a year ago. So, in March 2019, with the launch of our first mobile ticketing integration. So, we've gradually added about 15 other agencies since then with deployments taking as little as a week. We've gradually added our other bike-share integrations and we're working on more. Now, this is when I want to talk to you about today. My favorite kind of presentation, it's always been those where people share how they built their project and their product, what they were thinking during the development, what their tech stack looks like and what the tools that they use are.
So, that's what I'm going to do for you today. So, I'm going to share a couple of thoughts that we've had while working on this project. Five, to be exact. And I hope they'll give you some ideas or point you in the right direction for your project or that you will be compelled to talk to me after it, to share your own experience. So, first point I want to talk about is asking for some help and specifically around PCI. So, we'd been working on this for a while in some form or other. We'd been involved in the payment chain before but our volume was quite low at the time. And we only had to do SAQs before as a useful self assessment questionnaire because our volume were super low. Once we were going to launch new markets with our new Transit accounts, we wanted to be able to scale really quickly.
So, we knew we had to step up our PCI game and we had to find a company in a QSA to help us with that project when we started in 2017. So, the first thought I want to share with you today is not a technical one. It is quite simply, choose your QSA wisely. It's not something that we did at first. So, being a gang of start up tech people, we interviewed a few people and started a relationship quickly with one whose credentials seemed good because we want to tweak up the project rapidly and we wanted to get going and start coding.
You really want a QSA that will love to tackle the challenges of building a wallet, which may seem a bit ridiculot, a bit ridiculous, sorry, but it took us too long to realize that we had started to work with someone who was still stuck into 2010s. They didn't really know the modern players. They insisted that we look into hardware. No, you want a QSA that understands what you want to accomplish. That asks the right questions and finds the right solutions instead of saying, "No we're suggesting elaborate scenarios." So, we fired those and we found a new one that had the same objective as us and we wasted a bunch of time because of that because we didn't research our vendor correctly.
Second thing I want to share with you is how we researched our computing platform for this project. At that time, Transit was operated solely on Bare-Metal. When I joined Transit in 2016, the whole stack ran on three or four machines. Bare-Metal offered great performance at a great price, but the PCI controls we would have to put in place on Bare-Metal that we completely administered ourselves would soon drown us in work. We were less than 20 employees or so at the time, or it would completely bankrupt us in compliance product fees. So, we had designed the Transit account tech stack to be a couple of services, one identity provider, a proxy and a fault. And also since the bulk of our users are in North America, we knew that the traffic would mostly be flat during the night and peak, maybe two times during the two rush hours everyday. Thus, it made sense for us to choose a function as a service platform.
So, FAS for those not familiar function as a service is a... It's a category of cloud computing that allows us, that allows you to run some code. So, that would be the F the function in "function as service". So, to run that code somewhere and not actually manage the infrastructure at all, that's the "as a service" part. These services, normally they scale up and down automatically and you pay for what you use only. So, there is no charge or almost no charge during the night for us, because almost nobody rides the bus at night. And we can scale painlessly during the two rush hour peaks.
We researched this field a bit and we settled at that time on AWS Lambda. So, at the time Lambda was the more mature solution for a function as a service and all of the moving pieces around it were already included in AWS's PCI compliance program. So, this made our jobs a lot easier. We also use the serverless framework to manage deployments. It's worked fine, despite a few hiccups, mostly in community contributed plugins, but the framework itself works fine. It allows you to deploy stuff really easily. You just have one config file and everything works fine. AWS Lambda has worked flawlessly too. A common problem with function as a service platforms is that the first request you do for a day or after your service has been idle for a while, will often take a lot longer as the stack is warmed up.
So, the resources are allocated and a subsequent request would be quicker. We're lucky to have a pretty large user base and we get enough requests during the day so that our Lambdas never really go cold, except overnight. You do get the performance facts that may look high compared to operating Bare-Metal servers. Let's say maybe a hundred, 150 milliseconds per request, but it's worth it for the ease of usage. We're also using AWS as DynamoDB to store the account's data. It's super fast, provides encryption at rest. It's really, really easy to scale, and it's a great product.
Now we're building a wallet, so we need to encrypt some stuff. So, we've taken a look at a bunch of approaches for crypto. And by the way, every time I talk about crypto, I feel like this. I feel like this is a screen grab from the first matrix. I feel like that. So, we got some help there too, because crypto is complicated. We all know a bit about it, but it makes sense to hire people that are specialized for that. So, here's a scheme that we ended up using. It's only a small part of it, but it'll give you an idea of how we encrypt things. So, in order to be flexible, we've decided to divide personal information like three tiers, so critical information, private and personal information. So, what we call critical is the payment token itself. So, which in this case, you will have guessed that it is a Spreedly token.
So, Spreedly already does an amazing job at protecting the actual credit card number. But we went one step further and safeguarded it as if it were a cardholder data anyways. So, here it gets a bit technical and I've added colors to the arrows so it's easier to follow. So, let's say we've got the payment token completely on the left. This is the thing that we want to encrypt. So, first we're going to use a key that we've generated. So, let's call it the data encryption key or the DEK, and we're going to match it with an initialization vector, which is another generated string. So, these two will be used to encrypt the payment token using a aes-128-ccm algorithm. We'll store the IV with the encrypted payment token so we can get it back later. So, that's the operation that's described with the blue arrows.
Now, we want to encrypt that data encryption key with the help of the user's password. So, we will generate a salt, which is a random stream, which will be public, and we're going to match that salt with the user's password. So, now we're in the green with the green arrows. So, we're going to send this through with the derivation function, the green arrows to create a very, very long key, which we're going to split in two. One of the part of this key will be used as a key encryption key to encrypt the actual key and the other part we'll talk about later. So, the key encryption key is matched with the original IV and using those two, we will encrypt the data encryption key. So, this is the orange arrows there. So, now we're going to use the second part of the derived key as a password validation key. We'll store that as a user's password on our server.
So, when our user wants to sign in, we're able to recreate because they'll have the salt, because it is public, they'll have their password. We'll be able to combine to create the derived keys, split it in two, send the password validation key in our server, compare it to what we have in the user's account. If it matches then fine, we can send them the encrypted information in the IV, and then they'll have the key encryption key. So, they'll be able to decrypt the data encryption key and decrypt the actual token. They'll be able to do the whole thing. So, I said before, we had three tiers of information. So, the second tier is private information. So, it includes for example, partner credentials that the user has given us to use the service through Transit. So, for example, you sign into a bike-share service and we need to ask for your username and password. We are going to encrypt those of course, and we consider those private information.
So, those, they get encrypted in a similar fashion as what I've just told you for the payment token, except that we will not use the user's password itself. We will use a generated password so we can store that password on the client, on the device. So, we can use it without asking you. So, the whole point of encrypting the payment token with the user's password is that we want to put ourselves in a situation where there's no way for us to actually decrypt the payment token and use it without your consent. We absolutely need you to enter your password for that. For signing in, for example, to your bike-share service, it's fine. We don't need to ask for your consent. You've signed in once, it's fine. We can do it for you periodically to get your member or things like that.
So, this is private information and the rest of the information... So, personal information, everything is encrypted at rest in the database. Of course, as you would expect, the encryption is done client side, the user's password never touches the server. The scheme is super flexible. It's kind of easy to get the hang of it once you get the basic principles. And like I said, at the start, one really important thing here, hire someone to do code review. We did hire someone to do code review and test the heck out of this implementation. We're all somewhat smart people, but unless crypto is our full time job, it's better to have someone else to take a look... To hire someone that's a professional.
Now, the fourth point, make it easy to use. So, design is a central at Transit. We pride ourselves on our elegant interfaces and elegant UX. And the user experience of the Transit account was something that was extremely important for us. The accounts must be convenient and easy to use and design is a capital part of commerce like UI and UX, both. There's been a lot of research and iterative work that's been done by our teams of designer. They've worked incredibly hard to create a set of components and a visual language that would be modular and reusable. The broad order to dismiss of conflicting information requirements from our partners signup [inaudible 00:18:41].
So, I mentioned them with the same resolved looks of our hairdressers, that our hairdressers will have when they meet us and our unkempt hair on the other side of this pandemic confinement. So, with their help, we've also instrumented the whole thing using an analytics platform. So, we use Amplitude and their product designers can analyze user behavior, optimize funnels and analyze AB tests from there. So, the keywords here is "modular", "reusable". When you sign up for a new service, we might be able to drop some screens in there, but they must be modular and we must be able to reuse them between the partner integrations.
On the engineering side, we went through a lot of iterations before finding the right text fact to bring that division. So, now we're talking about the front end. So, to bring that division alive, we'd been doing integrations in the past natively on both platforms on iOS and Android. While that worked great, it was kind of difficult to keep those in sync and we had to do the work twice as well. So, we definitely wanted to keep both apps separate in their own language, but we were curious about finding a way that the business logic itself could be common to the two... Could be stored in a common place and be used by the two apps.
So, we decided to experiment with web technologies in the app. So, our first choice was using JavaScript. So, we decided to have all of the business logic programmed in JavaScript, but the UI built natively. So, this wasn't the context of our... This project that we tested this structure on was our ride hail integration. So, being able to order Uber or a Lyft in the app natively and follow your ride and everything. So, the whole UI is native but the business logic [inaudible 00:20:40] JavaScript
We created a packaging system. So, the app would download those packages and they would contain all of the business logic that would drive the UI. So, the idea here is that updating this web package would be a lot quicker than an actual app update that we have to submit to Google or Apple. And this package also could be used offline, like the app itself can, as opposed to having to call an API directly on our server. It worked well, but we thought that we could probably simplify things a bit and handling the UI itself would whip technologies as well. This way, the whole usage or purchase flow for our partner could be updated without an app update. And because, I mean, things change, requirements change partner APIs change, and you don't want a bug, or a partner changing something without telling you, preventing you from making sales.
So, being reactive is super important in that situation. So, this project went well, but now was the time to rebuild our bike-share flows. And we decided to experiment with that there. So, first thing that we did is that we chose a web framework. So, we chose React and then we used what seemed convenient at the time, we used Redux, which was kind of the suggested state management framework that was suggested by React. So, we ran this in production for a while.
So, we redid the whole signup flow for our bike-share systems and it ran well for a while in production. We fixed a few bugs, added new features, but we soon grew tired of the verbosity of Redux, the state management library that we were using. The way that it's structured is that it has actions, which you need to call them when you want to mutate your... Well, not actually mutate your state, but when you want to change something in your state to signify your intent of performing an action, and then you have to implement reducers, which will create a new state after that action.
And this took a lot of time. It's super verbose. So, a lot of code. So, we set out to find a better solution and we experimented with another solution in other integrations. So, at that time we were starting to work on our mobile ticketing integrations. We were, I think, in the start of 2018. So, it was a good time to try something else for state management. So, that something else was MobX. Instead of having multiple pieces that work together like actions and reducers and everything. MobX allows you to build classes that more or less look like models.
If you come from the MVC world, this is really reassuring. So, this did end up working very well and greatly reducing the number of lines of codes and of complexity in our application. So, we ended up rewriting the whole thing a third time. So, this time while keeping MobX, but also while developing an internal framework to help us create new integrations more quickly. It's been developed in a modular fashion so that when we talk to new partners and we understand what they want to integrate, we can reuse signup steps, already developed depending on what they need. So, we can drop in, for example, the promotional code step, if it's needed, or a billing address step.
Now, one thing that's been made a lot easier in the recent years is making UI and animations with web technologies and especially thanks to great libraries like Framer Motion that we use a lot in our integrations. They even have a module that can be used by UI designers so that we can share the animation. It's made this extremely, extremely simple. One last thing that I think is worth mentioning concerning our tech stack is that in our last rewrite, we ended up using TypeScript instead of Vanilla JavaScript. So, TypeScript might not be the best for all projects, but we're convinced that it is the right fit for projects like ours, where you need to interact with a lot of third party APIs. So, we built Transit accounts so that the front end interacts with the APIs directly of our partners. APIs come in all shapes and sizes like us.
They carry the marks of time of legacy implementations and bad choices made by early employees. They carry those on their face, like us and a part of building a successful wallet is also making sure that your developers can prevent bugs while working with these weird APIs while they add new features or they update the integrations. So, the finding types for a partner API and TypeScript ensures that you never lose 15 minutes finding what an account status of OW means for that specific partner, for example. You've typed it and your ID will just show you what it means. It's documentation as code as it should be. It's a great experience. A great developer experience.
Now, the last point is about flexibility. One thing that was definitely important for us, is to be strategic in our position on the market. So, we're not a merchant. We don't sell anything. We stand in the middle between users and our partners. So, we know operators want ownership of the accounts. We've heard that over and over. Every potential partner wants to know if they can get the full user account with the actual payment method. They don't want to lose anything if a partner goes belly up and we completely understand. That's why we've designed our accounts to send a user's data over to our partner during a signup, and the partner can retain the whole account.
How's this done? There's a couple of scenarios that I'm going to show you, but we've had to build this flexibility and the way that our identity management is done. So, of course, we have a central account where users can authenticate. They have username and a password, and certain partners want us to act as the identity provider. So, they'll have a shell account in their infrastructure for the user with their payment method and all, but they don't want necessarily to have to deal with username and passwords. They can always convert those shell accounts to full ones, if we ever part ways, for example.
For authentication in our integrations we've chosen to use JSON web tokens, GWTs or Jutes as a mechanism for our authentication, and also to share it with partners. So, the JWTs are self contained and they can contain claims. So, claims can be emails or other stuff describing a user account. And they're guaranteed to be legitimate because you can sign the JWTs and we make our public keys available to our partners. So, they can validate the signature when they actually get the GWT and they can make sure they can trust the JWT.
So, this is one scenario. Another scenario is a traditional one where a partner would want to keep using a username and a password for their service, many bike-shares do that. And it's fine too. We've decided that we can let users sign into those systems. We'll store their usernames and passwords encrypted in encrypted form in their Transit accounts. So, when a user signs into bike-share X in Transit we'll ask for their username, password. If they sign up, instead, if they don't already have an account and we're making a sign up for them on their behalf, we will generate a password for them, which we'll store in the wallet as well. And we'll allow the user to show that password if they want to be able to copy it and put it somewhere in their password manager, or put it on a post-it or something.
So, since we've previously recorded the personal information used in other signups. So, we can match it whenever there's a new signup. So, since we generate the password for the users and we probably already have a bunch of information that's needed for our signup, this can be extremely quick. And it can probably be in only one click
Flexibility. It's not only a question of authentication. It's also a question of payment. So, account creation wouldn't be complete without actually sharing a user's payment information with the partner. This is an integral part of the value that we bring to our partners. So, you won't be surprised to hear that we use Spreedly's platform here. So, when a user wants to signup in the context of a purchase to a Transit account. So, the first thing that we do after having created this account is to tokenize the card with Spreedly using this Spreedly's iFrame and then they store that card in their vault, and we get a token back.
We deal with a variety of different partners with extremely different APIs. So, some of them want the actual credit card number, like in the clear on signup. We're going to use a Spreedly receiver for that, that we can provision. So, the Spreedly receiver, you send a token to that receiver, the token that you got from the iFrame before, when tokenizing the credit card number, and then Spreedly will then take the actual credit card number and send it to our partner, to their API.
We have partners that do want that credit card number, but they don't want it in the tier. They want it to be encrypted, using a public key that they choose. So, on every session we would call for a user, we would call an endpoint that will give us a public key from that partner. And we will use that public key, which will be different on every session. So, we will use that public key every time a user does a purchase to encrypt the credit card. We'll send the public key actually to the receiver, to Spreedly's receiver, and they will encrypt the credit card number before sending it to the partner. So, some partners, they want the credit card every time a user does a purchase and others want it only one time on signups. They'll tokenize the card themselves and it's fine. We can work with both scenarios.
There's also another scenario, which is super interesting. There's newer companies that don't want to touch the credit card number at all. They use Stripe or others providers and they are happy with letting us use their Stripe gateway instead. So, this case is super interesting. A partner will create a restricted-use Stripe API key that we can use to only add cards and create customers in their accounts. So, we'll provision a Stripe gateway in our Spreedly account and we'll be able to create customers and add cards to that account after that, using that provisions Stripe gateway. So, during a purchase or a signup, we will tell the Stripe gateway to create a customer or a card and then the Stripe gateway from the Spreedly infrastructure will communicate with the Stripe account of the partner. And then they will send us back the new customer and card IDs.
And then we will send that new customer and card ID to our partner, and they'll be able to associate it with the user account that was newly created. So, this way they'll be able to charge the user when they want. And there's nothing left on our side, except the ID to the customer. They'll have complete control over the account. So, this is also a super interesting scenario. So,that's it. That's the five things I wanted to share with you today. So, there, ask for help. PCI wise, research your computing platform, look into crypto, hire someone to help you with that, make your platform easy to use, and also be flexible. I'd love to chat if your company is working or has worked on a wallet before. And I'd love to hear about your experience. If you want to reach out, here's how, you have my email there, agregoire@transit.app or also on Twitter. I want to thank to the Spreedly folks one last time. This will be a great experience and thank you for listening and have a great day.