System requirements
Functional:
- Create account for users.
- Create payment methods of the supported integration with card issuers.
- Create the ability to create account keys to securely integrate in other platforms
- Create payment links for users
- Create payment forms for businesses which require website integrations
- Create security and payment signing
- Create expirations for the links
- Users can see their history of transactions with their statuses.
- The users should be notified about transactions statuses.
- The users can add the correct currencies for card issuers/banks
Non-Functional:
Reliability is crucial for this type of platform since the users should trust that the payment is done successfully
Strong Consitency, and durability Is one of another key feature since a success or a failed payment should clearly indicate it and should be not lost.
Security should be strong to avoid unwanted payments. Encryption with expiration on the payment links should be included to be able to identify that the link is generated by the platform. Here the signing of the URLS should be included to assure authenticity including the expiration.
Capacity estimation
Let's suppose the system will have 1 million active users, that will generate about 10 payments links each.
=> payment link entity is about: 256 bytes each
payment Id: 8bytes
payment method: 2 bytes
userId: 8 bytes
card issuer: 8 bytes
realm: 8 bytes
amount: decimal (8 bytes)
date: (12 bytes)
currency: 2 bytes
signing: 30 bytes
status: 10 bytes
-> 10 million transactions daily results in 2.5 million KB -> 2.5 TB daily
resulting 950 TB of data yearly.
-> 10 million of users data growing in 2 years -> 2TB of data
Aprox 2 Petabytes of data in 2 years.
API design
->Post users: create a user account
{
username: "",
email: "",
date: "",
password: ""
} -> responds 200 OK with link of the resources created.
PUT /api/users to add the payments methods.
-auth header with JWT
{
paymentMethods: []
} -> responds with 200 OK, 401 if unauthorized
-> POST /api/payment
{
amount: decimal,
date: timestamp,
paymentMethod: string/enum.
currency: string/enum
realm: string,// to allow payments to be groupped (site name).
}
with auth header containing auth api key. and secret, only to be exposed in backend.
Database design
I can use PostgreSQL because of the strong consistency that is offers for the users management. For payments I will choose a database with built in sharding and scaling like DynamoDB. The partition key for this database will be account id, also the transaction logs involved in a payment will be stored in the database along with payments.
High-level design
Gateway to authorize requests and build
LB - to load balance between multiple instances of the same service: users microservice
User microservice: to allow user managements and user account details
Payment microservice to generate payment links, and update status, coordinate transactions.
Validate Payment microservice: validate the authenticity of the
Payment transaction microservice.
Fraud detection analytics: all the payments links created are going to analytics database via CDC streaming built-in in DynamoDB
Request flows
- Create payment link hits apigateway, then LB, then payment links microservice then the database returning the signed payment link
- Do payment: will hit the verification payment microservice the Update the payment status and coordinate transactions
Detailed component design
- Sharding offered out of the box for payment service of the DynamoDb service.
- The payment service will need to coordinate the transactions to subtract from users transactions and using a distributed locking for payment to prevent double payment, also DynamoDB offers transactional updates within a row so we can implement a optimistic locking.
- Microservice architecture can be chosen to meet such scalability requirement, scaling each microservice individually
For the verification and creation of the links a cryptographic mechanism should be created to determine the authencity of the links. Private-public keys can be employed for this job, singing it with the public key and decrypt it with private. Shared secret can be used allowing only backend to backend communication
Trade offs/Tech choices
The tradeoff on using two for our system 1 for users PostgreSQL with offers strong consistency and one for payment and transaction logs which high volumes of data offering a boost of performance.
To scale the microservices Kubernetes can be used although the microservice architecture come with a lot of other challenges like distributed transactions (coordinate transactions).
A deduplication process need to be introduced for clients which may retry the payments.
Failure scenarios/bottlenecks
The common use case is the failing of the databases but since I try to use managed instances of DynamoDB and PostgreSQL in RDS.
Other failure I see a lot happening is the browser of the payer closes before the payment finishes.
Multiple retry of the same payments.
A rate limit can be also configured so the microservices are protected.
Since we are sharding by user account Id the problem o having a hot shard microservice
Future improvements
- Fraud detaction algorithms using AI.