System Design Case Study: Payment System
A reliable, scalable and flexible payment system is essential.
According to Wikipedia, “A payment system is any system used to settle financial transactions through the transfer of monetary value”.
Step1: Scope the problem
Some good clarification question to ask:
- what kind of payment system are we building?
- do we handle the credit card payment processing ourselves?
- do we need to support different currencies and international payment?
- do we need to handle pay-out flow?
Let assume the key features to cover:
- pay-in flow: payment system receives money from customer
- pay-out flow: payment system send money to sellers
- reconciliation process between internal service and external service is required.
Step2: High level design
Pay-in Flow
After buyer places an order, the money flows into Amazon bank account, which is pay-in flow
Components
-
payment service: accept the payment events from user and coordinate the payment process.
-
payment executor: execute a single payment order via Payment Service Provider (PSP), A payment event could contains multiple payment orders
-
PSP: move money from account A to account B,
-
Ledger: keeps a financial record of the payment transaction. for example, seller are recorded as debit 1 and buyer are recorded as credit 1
this is important for post payment analysis.
-
Wallet: keeps the account balance of the merchant.
Work Flows
- user click “checkout”, payment event is generated and sent to payment service
- payment service stores the payment event in the DB
- payment service calls the payment executor for each payment order
- payment executor store the payment order into DB
- payment executor calls external PSP to process credit card payment
- after executor success, payment service update the wallet to records how much money a seller has
- after wallet service successfully update, payment service calls Ledger to update
- ledger service append a new ledger information into DB
Data Model
We need two tables for payment service: payment events and payment orders.
When we select for a payment system, performance is usually not the most important factor, instead, the correctness, stability are more imporant.
So relational DB is more suitable solution in our case.
Double Entry Ledger System
an important design principle in ledger system: double entry
it records every payment transaction into two separate ledger accounts with the same amount.
The double entry system states that the sum of all transactions entries must be 0.
Hosted Payment Page
Most company don’t want to store credit card information because they need to deal with complex regulation.
To avoid, companies use hosted credit card pages provided by PSP.
PSP provides a hosted payment page that captures customer card information directly.
Pay-out Flow
Components of pay-out flow is similar to pay-in flow.
Instead of using PSP to move money from buyer’s account to ecommerce website account, this time, move money from ecommerce website account to seller account.
Step3: Design deep dive
PSP Integration
- user click checkout button, client calls the payment service with payment order information
- payment service send the payment registration request to PSP, payment can only register once, we need to ensure it only performs once.
- PSP returns a token back to payment service, we can examine the status of payment later use this token
- payment service stores token into DB
- client display PSP hosted payment page. The page using the token above to fetch the details of payment
- user fills the payment details. PSP start payment processing
- web page redirect to confirmation page.
- Async, PSP calls the payment service with the payment status via a web hook. Webhook is an URL on the payment system side that was registered with PSP, when payment system receive the update, it extract the payment status and update the record in table.
Reconciliation
When using async mechanism, there is no guarantee that a message will be delivered. How can we ensure the correctness?
The answer is reconciliation. This is a practice that periodically compare the state among related service.
Every night, PSP or bank send a settlement file. settlement file contains all the transactions and account balance.
The reconciliation system parse the settlement file and compare the details with ledger system.
Step4: Wrap up
In this article we explored how to design a payment system, where starting from buyer checkout, and end with send the money to seller’s account.
We need a payment service to store the details of payment order, then leverage payment executor to communicate with PSP to register the payment event.
PSP will return a token for future reference, then we can redirect user to hosted payment page to input their payment information.
As long as PSP complete the transaction, it will redirect user to payment confirmation page, also notify payment service with status update.
Payment service then update Ledger and Wallet service to reflect the change.
We also mentioned the reconciliation process, which to prove the overall system is correct.
Here are some of my takeaways:
- When we need to avoid duplicated operation, we need a global unique ID and use it as dedup key.
- Hosted payment page is fancy
- The usage of webhook between PSP and payment service