Unified Checkout: Streamlining Uber’s Payment Ecosystem
Uber started as a ridesharing company with a simple yet ambitious mission: make transportation as reliable as running water, everywhere, for everyone.
When the company focused on ridesharing, most back-end systems were built around a trip, a rider, and a driver. Since it was the only LOB (line of business) at the time, any payment-specific operations were implemented directly into the ridesharing system. For example, supporting Apple Pay® was implemented on the Rides back end.
When Uber grew as a company, so did the number and complexity of LOBs. Uber started offering food and grocery delivery, bike and scooter rentals, public transportation tickets, and car rentals. Each LOB built their own system on the back end where their business logic lived. Still, they all needed payments and risk support, and that required specific operations like 2FA and fingerprinting. Implementing payment business logic became expensive because any change had to be made many times across services. This harmed velocity and feature parity, and increased the complexity, cost, and risk of maintaining services.
Payment methods weren’t uniformly supported across user flows and LOBs. For example, Apple Pay wasn’t supported in every user flow or LOB at Uber. You could use it to pay for an on-demand ride, but you couldn’t use it to pay for a scheduled ride until very recently. Other payment methods like Google Pay™ and direct bank transfers were in the same situation. That was because a different service handled scheduling rides, and it followed a different model of securing funds before fulfillment. Upfront charge payment methods and ones that required any form of 2FA to initialize or finalize a payment weren’t supported by some LOBs or flows, like scheduling a trip, switching the payment profile for an ongoing trip, or even changing the payment method after the completion. This was especially important for popular payment methods in markets like PIX in Brazil and UPI in India.
After years of having to implement payment methods individually and once per LOB, we were determined to find a better solution for the next compliance requirement where we’d need to repeat the same change multiple times across back-end services. The trigger point to fund a team and solve this problem was Strong Customer Authentication, which was a mandate initially rolled out in the European Union. It dictated that any transaction with a European credit card that was higher than a certain threshold would be subject to a multi-factor authentication process using 3-D Secure to confirm the shopper’s identity. The protocol involved fingerprinting the shopper’s device and had a chance to throw a challenge for the shopper to complete, like a biometric challenge or a one-time password.
We had around 70 endpoints that might result in a financial transaction, all of which integrated directly with our risk and payment systems. This blog describes how we implemented these authentication methods and supported payment methods while dealing with this complexity.
We built a checkout layer to allow LOBs access to the Uber payment ecosystem via one connection. This introduced a new layer in our systems to hold checkout business logic and act as an orchestrator. Any payment method support or need for a cross-LOB change can now be implemented once by a single team.
Figure 1: Uber LoBs without and with Unified Checkout.
All of the business logic to support payment methods and their specific operations moved to this new layer. If Unified Checkout supports a given payment method, all it takes to enable it on an LOB or flow is a few configuration changes.
New payment methods are only supported fully in Unified Checkout, which serves as a moving force for adoption across the company and delivers value to existing consumers. The strategy of the Unified Checkout platform was to treat each Uber LOB as an external company that we knew very little about, similar to existing checkout offerings in the market. By decoupling the system from the LOB’s product layer, we keep the platform agnostic of their inner workings and only aware of payment method specifics. This allows the platform to evolve faster and pushes the team to model all operations in a generic way while ensuring that people can pay with their preferred payment methods in any flow across all Uber LOBs.
Unified Checkout is available in a modular and hosted version with client components that are coupled with the checkout back-end service.
It provides generic payment method support for global and local instruments, a risk system integration on the client side and on the back-end side for risk evaluation, and the execution of arbitrary flows through a generic actioning framework called Checkout Actions.
A small set of microservices handle the checkout request and execute a set of operations:
- Prepares the payment profile by exchanging transactional data captured on the client side with other payment systems. This can be passing a short-lived token all the way to the third-party processor, extracting authentication identifiers from the request, or initializing the request to generate a 2FA URL.
- Invokes the risk system to score the request and later secure the funds on the person’s chosen payment profile either by placing an authorization hold on the funds so they can be captured when the order is completed or by charging the person upfront before the order is fulfilled, depending on the strategy decided by the risk system.
- Generates Checkout Actions to either guide the person through any necessary steps, or to display a failure message along with buttons that trigger corrective actions, like reauthenticating an account, editing the payment method details, or selecting another payment profile.
- If the risk system instructs Unified Checkout to initiate an upfront charge, it waits for the payment confirmation signal emitted from the payments platform once we get a response from the third-party processor. This is a necessary complexity, as most processors are asynchronous. Once Unified Checkout consumes this event, it either confirms the order’s preparation or cancels it by emitting another event or calling an LOB service directly.
Figure 2: Unified Checkout architecture diagram.
In the modular version, clients can integrate the pre-built components on their own screens and checkout funnel and invoke them throughout the person’s journey of checking out.
In this integration model, the LOB back end calls the checkout back end directly. Any Checkout Actions that need to be surfaced externally are then propagated through the response in an opaque manner and passed to the client checkout component. The payload is opaque to the LOB so that only the checkout back end and checkout client can process it. This was a deliberate design choice to prevent payment complexity from leaking to the LOB services and having those teams building features on top of the payload.
Figure 3: Screen owned by Rides LOB.
We offer the hosted solution to new verticals or smaller LOBs so that they can use a complete payments and checkout solution out-of-the-box. This accelerates their time-to -market and experimentation while maintaining the same or very similar level of payment method support.
With the hosted implementation, once the person is ready to check out, the checkout solution takes control over the application and displays a summary view of the order. On the same screen, the person can select their payment profile and place the order. Once the order is successfully prepared, a signal is sent to the client and to the back end, which will then dismiss the hosted checkout screens on the client side and give control back to the LOB.
In this model, all the communication once the hosted solution has been invoked happens between the checkout client and checkout back end. This means that the LOB back end doesn’t call the checkout back end.
Figure 4: Hosted checkout summary view.
Checkout Actions is a stateless framework to execute arbitrary flows that are required for someone to convert successfully. These actions can be 2FA operations, clearing your arrears, executing an identity challenge, authorizing a payment operation, device fingerprinting, and more. The actions have to be generic enough that they can be applied across all LOBs. If there’s an action specific to a single business, like confirming your delivery address, then it shouldn’t be implemented as a Checkout Action. Checkout Actions may not require any interaction, and can be a data collection process or the initialization of a back-end process.
The client side runs the Checkout Actions and generates the results payload. This payload is sent to the checkout back end on the subsequent checkout request and any state required is carried along with it. This request can either succeed or generate more Checkout Actions for the person to complete.
Figure 5: Checkout Actions sequence diagram.
Pre-checkout actions are actions that must be executed on the client side before the first interaction with the back end for the checkout request. These are usually deterministic operations and can be generated by our back end in real time since their execution is already known to be a mandatory step. Someone checking out with Apple Pay will always need to pick a payment method in their Apple Wallet®, for example.
Figure 6: Pre-checkout actions.
Post-checkout Actions
Post-checkout actions are generated after the first interaction with the checkout back end. These can be operations required to authorize or complete a payment transaction or they can be rejection actions, in case of insufficient balance, an error while placing an auth-hold, or even a rejection by Uber risk systems or someone’s bank. Every checkout action generated by the system offers a corrective action, even if it’s as simple as allowing someone to select another payment profile and try again. There are also more complex recovery flows like confirming your identity, connecting a social media account to your Uber account, or going through an identity challenge.
Figure 7: Post-checkout actions.
Every launch of Unified Checkout goes through an experiment to measure its impact on metrics like checkout request conversion, session conversion, session recovery, and number of orders completed. After launching the most frequently used payment methods globally on Uber Eats, we performed a holdout experiment where a percentage of the participants didn’t get their requests routed to the Unified Checkout system and stayed in the legacy experience where their system connected directly to risk and payments. As a result, we observed that the Unified Checkout experience had a 3% higher checkout conversion rate and a 4.5% higher session recovery rate when compared to the legacy experience. This means that people saw fewer errors when checking out. Those that saw errors could recover from them and convert more often.
Even though this holdout experiment was only executed on one LOB, we extrapolated the impact to the entire order placement traffic and calculated an estimated impact of incremental gross bookings in the order of hundreds of millions of dollars annually.
Other than the lift in the metrics, every flow now supports at least the top 5 most-used payment methods globally. The biggest LOBs also support popular local payment methods, unlocking more availability. The lack of support for a specific payment method is now a strategic decision rather than a technical limitation, cost barrier, or complexity of implementation.
Following the strategy of treating Uber LOBs as external customers, the Checkout Platform team experiments with new experiences and features to identify opportunities to increase conversion metrics and decrease drop off. This includes new types of Checkout Actions, new error messaging models, more granularity with error messages, optimizing existing operations, new modalities for Memberships, scheduled orders, clientless flows, split tender, and more.
It’s natural for software solutions to have an inflection point in which common or duplicated operations must be transformed into platforms or redesigned in a more generic way so that it can be applied a number of times with minimum maintenance cost. You could call this the moment in which you are paying tech debt incurred due to the hyper-growth of the company.
At Uber scale, having a system that handles the entire order placement traffic from all mobile and web applications means that we can impact and improve the user experience from all of our users placing orders at once with a single change or deployment. Optimizing a checkout flow can have a significant impact on key business indicators, such as gross bookings.
It’s our job as Software Engineers to identify opportunities as such and to design solutions that address those opportunities in a way that’s beneficial to both users and the business, even if that means starting from a bottoms-up project and pushing for it until it eventually becomes a product.
Google Pay™ is a trademark of Google LLC.