Uber is developing a payment platform for India that enables operations teams to more seamlessly collect and distribute cash and digital wallet payments to drivers. In this article, San Francisco-based software engineer Yijun Liu reflects on his experiences working with the Uber India Engineering team in Bangalore to architect this revamped payment system.
As a member of Uber’s Payments Efficiency team, I am responsible for developing payment systems for our operations teams across the world. Each region we operate in handles payments differently, and as a result, no two payment systems are exactly alike.
Credit card usage is not as common in India and other Southeast Asian countries, so Uber built a cash payment system in 2015 to make our services more accessible to these markets. Enabling cash payments presents new challenges, from ensuring that riders have cash on hand to processing and collecting driver commissions.
Digital wallets are a second payment option for riders in India. Last October, currency demonetization caused digital wallet usage to skyrocket, but many providers have yet to mature and stabilize in their local markets. At the same time, identification verification technologies like two-step authentication and one-time passwords complicate the payments experience even further for riders accustomed to using cash transactions. While digital wallets are efficient and performant, there is still room for improvement from a rider experience perspective.
Since January 2017, I have been working out of Uber India Engineering in Bangalore to help the team rebuild and ship our payment system for India. In the following sections, I describe our previous system, outline its problems, and discuss how we addressed them by architecting a more seamless and convenient payment platform for our operations teams and drivers.
Commission collection in India
Since hard currency plays a huge role in the Indian market, one of Uber Bangalore’s primary focuses is to optimize our existing commission collection system for cash transactions. Although the existing system works well in some parts of the world, the popularity of cash transactions and diversity of payment scenarios across India necessitates a more scalable and configurable solution.
Conceptually, Uber’s payment system operates like a data pipeline that transforms trip payments (cash) into digital currency before being collected by a disbursement system that pays drivers either via direct deposit or digital wallet.
The flow of Ganges River, diagrammed above, illustrates this pipeline at a high level. The Ganges begins as a rivulet in the Himalayas, then flows through multiple cities like Rishikesh, Haridwar, Allahabad, and Varanasi. At each phase, the Ganges grows wider and deeper until its vast flows discharge into the Indian Ocean at the Bay of Bengal.
Similarly, the payment system data pipeline starts with basic event information (like trip duration and distance) that converts into money order details, then to settlement strategies, and finally to the actual collection or disbursement by the payment service provider, completing the transaction. When cash is factored into the equation as a payment option, the pipeline becomes lot more complex, and third-party digital banking services must be incorporated in a way that does not disrupt the system’s flow.
Adding cash to the equation
When we first launched our cash collection system in India, we collected payments from riders and paid driver-partners through our driver payout systems. Recently, however, Uber’s global growth and new business requirements called for a revised collection pipeline for cash trip commissions. To build this pipeline, we had to concatenate the cash collection pipeline with the driver payout pipeline and link several steps between the two.
This feat was challenging to say the least: as an analogy imagine joining the Sindhu River with the Ganges by digging canals; if these routes are not maintained, surplus water can easily flood them. Given the amount of cash transactions in India and Southeast Asia, the cash payment system inevitably floods with traffic. Beyond this, each region handles cash collection slightly differently depending on driver practices and business needs.
Our old payment system was unable to process cash commissions for two reasons:
- From a systems perspective, extra steps between the driver and rider pipelines could cause data inconsistencies. When the system saves state to the database, sends notifications, logs entries, and calls the next level service, partial failures during heavy traffic could result in inconsistent data even though these actions happen asynchronously through layers of try-catch blocks.
- From a business perspective, cities in India have vastly differing cash payment scenarios. They vary in per trip fare, total trip volume, collection channels, and cash handling preferences. Due to such differences, city operations teams need versatile ways to define and execute their collection strategies. Since the old system was more rooted in its collection practices, it could not scale for custom scenarios.
As depicted in Figure 2, event flows through this collection pipeline by: (1) writing an event to a database, (2) triggering multiple side effects (such as sending emails or SMS messages) asynchronously, (3) calling the next service’s interface to ask for further actions, and (4) logging these interactions. Although this flow sounds logical, there are three pitfalls to this design:
- When too many calls happen simultaneously, it can overwhelm the downstream service and cause it to time out. Regardless of downstream failures, the system writes data to the database, potentially causing data discrepancies between (1) and (3).
- Similarly, asynchronous calls to trigger side effects (2) can also overwhelm the system and time out. This may inhibit SMS notifications from being delivered to drivers, preventing them from receiving payment collection instructions.
- Since calls to the next service (3) are normally asynchronous, the system writes a log entry regardless of the result in the third connection point, hence irrespective of this logging behavior, it does not guarantee a success.
Building a next-gen payment system
To solve these problems and deploy a more advanced payment system, we needed to rebuild our system flow with an updated architecture and engineer greater flexibility into how we calculated rider payments.
Our architectural solution to the first problem was to reverse the calling flow with queueing. Instead of relying on a service to call all the methods at once, an independent service listens to the database events one-by-one. For each successful write operation, this service writes side effect instructions to the queue, and then both the side effect and downstream services simply listen, fetching data at their own speed.
In our new architecture, a sudden traffic spike only temporarily congests the queue, but never loses any data or fails to perform important actions. This design revamps the payment system architecture, as depicted in Figure 3.
Cash collection customization
To address India’s diversity of cash collection preferences, we needed to understand the ways in which these strategies differ between regions, including desired collection amount and frequency per driver and the order in which operations teams trigger side effects.
If we directly write business logic that customizes collection conditions into the backend code, we have to develop different flows for each region, which is both time and cost ineffective. More than that, every time a region needs to create, read, update, and delete (CRUD) its collection policies, we will need engineers to write and deploy new code, further depleting our efficiency.
To address these conditions, we deployed a configuration layer to represent all business logic and implement each side effect and state transition as unit modules. This segregates business logic at the very top config layer from the backend module code, thereby making both of them easy to reuse as components in a movable type system. Now, whenever operations teams need to modify the collection flow for their region, they only need to update the config layer. When the config updates, the collection system reloads flows for that region based on that config’s unit modules. Below, we demonstrate our new collection system mechanism:
Our update also solves user-facing issues. In the old system, we relied on SMS and emails to notify collection statuses to our drivers, and did not integrate local wallets and bank accounts in-app. While SMS and emails are a viable way to communicate updates to our drivers in other parts of the world, users may experience limitations in India and Southeast Asia for various reasons, including:
- They do not receive our SMS messages if local telecommunications companies misinterpret them as fraud.
- They do not have easy access to emails or infrequently check them.
- They need to log into their digital wallet to check their balances, which can be a time-consuming and inconvenient process.
To address these three concerns, we integrated driver wallets into the partner app itself, alleviating the inconvenience of having to switch between the two apps to check your balance.
Additionally, we polished our communication channels, heavily shifting from SMS and emails to localized in-app notifications and in-app pages per region. This new functionality makes it easier and more seamless to share important information with drivers about payment collection.
Engineering payments beyond India
These backend and frontend changes to our cash payment system provide the flexibility and user-friendliness necessary for Uber to build flexible transportation solutions in India and Southeast Asia.
Uber India Engineering’s redesign of cash payment and collection systems has far-reaching effects beyond our own users; our innovation in payment technologies is at the forefront of digitizing India’s payments world. In addition to building out our payment systems, our Bangalore-based team also focuses on driver growth, rider experience, vehicle technology, and maps projects, all challenging—but rewarding—fields with huge implications for India’s massive and emerging market.
Interested in working on engineering challenges that boggle the limits of scale? Consider applying for a role on the Uber India Engineering team.
Yijun Liu is a software engineer on Uber’s Payments Efficiency team and Mrina Natarajan is a technical program manager on Uber’s Developer Experience team.