How Paddle built a unified payment lifecycle to standardize integrations with multiple payment service providers, enabling intelligent routing and better acceptance rates.
This is part one of a two-part series on payment routing, where we explore how we built payment routing. Read part #2 here.
As a merchant of record, we use various payment providers to efficiently manage payments across different regions, ultimately driving better results for our users.
In this blog post, we’ll explore how we achieve this, the complexities of integrating with multiple payment service providers, and how we developed a unified payment lifecycle for our users.
How payments work in Paddle
Paddle offers multiple payment methods for customers at checkout. Behind the scenes, we integrate with several independent payment service providers (PSPs) to make this possible. What you might not know is that Paddle also holds several merchant accounts in different regions around the world where we accept payments.
A merchant account is a single bank account in Paddle’s name that is attached to global payment networks with the help of a PSP. The concept of opening merchant accounts to accept payments nearer to the end customer is called local acquiring.
Local acquiring brings some big benefits for users who process international transactions. It means that payments are often considered domestic, because both the acquiring bank and the issuing bank are within the same country or regulatory region. This results in better payment acceptance rates, as domestic transactions are more likely to be approved over international ones, plus lower fees from the payment networks.
In some cases, we have integrations with multiple PSPs in the same region. At first, this might seem redundant because we’re already achieving local acquiring with one PSP, but there are some key benefits:
- Different providers offer different payment methods. This lets us enable more alternative payment methods for our users.
- We can route to the best provider. We’ve found significant differences in acceptance rates between PSPs even in the same regions, which is most likely a result of providers using different acquiring banks.
- Redundancy is crucial. If a PSP has a reliability issue, an outage, a regulatory issue, or even goes bankrupt, we have alternative providers to maintain consistent service to our users.
To get here, we needed to build a system that was capable of choosing which destination a payment should be processed on. A “destination” is a merchant account in a specific region using a specific PSP, and we call this process “routing”.
---
title: Why do we need to route payments?
---
graph TD
subgraph PM ["Payment method"]
A[Apple Pay]
B[Card]
C[PayPal]
end
A --> PR[Payment routing]
B --> PR
C --> PR
subgraph SP ["Service provider"]
PSP1[PSP A]
PSP2[PSP B]
PSP3[PSP C]
end
PR --> PSP1
PR --> PSP2
PR --> PSP3
subgraph MA ["Merchant account"]
MA1[💳 Account A]
MA2[💳 Account B]
MA3[💳 Account C]
end
PSP1 --> MA1
PSP2 --> MA2
PSP3 --> MA3
classDef paymentMethod fill:#f9f9f9,stroke:#333,stroke-width:2px
classDef routing fill:#ffab91,stroke:#ff5722,stroke-width:2px
classDef psp fill:#bbdefb,stroke:#2196f3,stroke-width:2px
classDef account fill:#f9f9f9,stroke:#333,stroke-width:2px
classDef header fill:#333,color:#fff,stroke:#333,stroke-width:2px
class A,B,C paymentMethod
class PR routing
class PSP1,PSP2,PSP3 psp
class MA1,MA2,MA3 account
Routing is just half of the story. We also need to be able to:
- Accurately measure performance for all payment methods across all PSPs in all regions to fully understand the impact of our payments strategy.
- Quickly change our payments strategy over time to improve performance, without requiring engineering changes, and to run experiments to influence new strategies.
- Add integrations to new destinations quickly and immediately measure performance accurately to maximize benefits.
To do this, we needed to standardize all of our integrations with payment service providers into one unified lifecycle.
Unifying the payment lifecycle
Paddle Checkout provides a choice of payment methods and each payment method might require different PSPs to support local acquiring.
Here’s an example of a simple hypothetical payment lifecycle. Paddle’s is a bit more complicated, but follows the same principles.
---
title: Payment lifecycle
---
graph TD
A[Screening] --> B[Authentication]
B --> C[Authorization]
C --> D[Captured]
D -.-> E[Refund]
A --> F[High risk]
A --> G[Expired or invalid card]
B --> H[Auth failed]
C --> I[Declined]
C --> K[Failure]
I --> J[Retry]
K --> J[Retry]
J -.-> A
classDef success fill:#e8f5e8,stroke:#4caf50,stroke-width:2px
classDef failure fill:#ffebee,stroke:#f44336,stroke-width:2px
classDef process fill:#f9f9f9,stroke:#333,stroke-width:2px
classDef optional fill:#fff3e0,stroke:#ff9800,stroke-width:1px,stroke-dasharray: 5 5
class A,B,C process
class D success
class F,G,H,I,K failure
class E optional
class J process
This payment setup can get quite complicated on its own and becomes a huge challenge when developing a routing system on top of it all. What we need is a consistent data model and API contract for all of our payment methods and integrations.
This is important because:
- We want to directly compare different payment integrations fairly against each other. It’s not useful to route payments to one destination over another if we’re not confident that they perform better.
- We want to be able to add new payment methods, PSPs, and regional merchant accounts over time, and they should be able to take advantage of the existing routing, analytics, and retry features.
- We want to provide consistent and meaningful experiences for all Paddle users when they interact with payment data in the dashboard, APIs, and reports. For example, when understanding payment decline reasons.
Developing this was an engineering challenge, but one that we considered truly worthwhile to unlock the value of local acquiring. We’ve done this work so our users don’t have to.
The first challenge to overcome is the technical complexity involved in standardization because every PSP integration is different. Some have SDKs, others don’t; some use XML, others use JSON; and they all have varying meanings attached to an HTTP status (or none at all!). Some necessitate a complex PCI-proxy setup to keep card data safe.
We found that the main ingredients required to unify these integrations are:
- A suitable data model to implement the “lifecycle” of a payment as a state machine.
This is used for both the transactional processing of payments, and also as the basis of an analytics model which Paddle can use to compare the performance of all potential integrations. - An API to move payments through this lifecycle.
It needs to be simple enough for other Paddle systems to interact with, but capable of accommodating specific features that might vary depending on payment method or the nature of the payment. For example, card payments require a unique approach to fraud detection, and also necessitate 3D-Secure. - A way for each integration to describe the final outcome of a transaction in a shared language across all integrations.
We call this “response mapping”. This lets us understand why a transaction ended up with that particular outcome. A very common example of this is being able to identify an “insufficient funds” decline, or being able to differentiate between payment declines and system failures that can occur downstream.
With a unified payment lifecycle, we’re able to standardize payments from different PSPs, measure performance, and build a rules engine that determines how each payment gets routed.
In the next blog post, we walk you through our rules engine, the two strategies we currently use, and outline some challenges and plans for payment routing at Paddle. Read it now.
About George Wilson
George Wilson is Head of Engineering for the Payments Group at Paddle, leading teams that build, maintain and scale our payments stack.