How Stripe Prevents Double Payment Using Idempotent API
#45: A Simple Introduction to Idempotent API (4 minutes)
Get the powerful template to approach system design for FREE on newsletter sign-up:
This post outlines how Stripe implements idempotent API. If you want to learn more, scroll to the bottom and find the references.
Share this post with somebody who wants to study system design & I'll send you some rewards for the referrals.
Note: This post is based on my research and may differ from real-world implementation.
2010 - California, United States.
Two brothers want to run a business.
Yet they found it difficult to set up an online payment for it.
So they pivoted to create an online payment service and called it Stripe.
As number of users grew, double payment became an issue for them.
That means, mistakenly charging a user twice for the same transaction.
Here are some reasons for this:
1. Server Error
The request fails while the server processes it.
But the client doesn’t know whether the request was successful or not.
So it isn’t safe to retry.
Otherwise, there might be a double payment.
2. Network Error
The server processed the request successfully.
But the network connection failed before returning a response to the client.
So the client doesn’t know if the request was successful.
Hence it isn’t safe to retry.
Otherwise, there might be a double payment.
Hungry Minds (Featured)
Learning system design is about staying hungry.
Hungry Minds scans 100+ sources for the best deep dives, trends, and tools for software engineers to stay ahead.
Join 9,001+ engineers from big tech to startups for 1 free digest every Monday.
Idempotent API
They wanted to solve the double payment problem in the best possible way.
And as in most cases, the simplest solution is the best solution.
So they created an idempotent API.
It guarantees that a specific request can be retried many times without side effects.
That means a specific request gets processed exactly once even after many retries.
Here’s how Stripe implements idempotent API:
1. Idempotency Keys
They should process a request only if it wasn't processed earlier.
This means they must track requests processed by the server.
So they create a unique string (UUID) to use as the idempotency key.
And send it with each request’s HTTP header.
Also they generate a new UUID whenever the request payload changes.
Imagine the idempotency key as a fingerprint to find whether a request has already been processed.
They store the idempotency keys with an in-memory database on the server side.
And cache the server response after a request gets processed successfully.
So the in-memory database gets queried to check whether a request has been processed.
They process a request only if it's new and then store its idempotency key in the database.
Otherwise, the cached response gets returned. This means the request was processed earlier.
Also they roll back a transaction using the ACID database when a server error occurs.
Besides they remove the idempotency keys from the in-memory database after 24 hours.
It helps to reduce storage costs and gives enough time to retry failed requests.
Put another way, an idempotency key could be reused after that period.
2. Retrying Failed Requests
Although it’s safe to retry using an idempotency key, there’s a risk of server overload with many requests.
So they use the exponential backoff algorithm.
That means the client adds an extra delay to retry after each failed request.
Besides a failed server could experience a thundering herd problem when many clients try to reconnect at once.
So they use jitter to add randomness to the client’s waiting time before a retry.
Idempotency is essential to reliable online payments.
Also it offers better security and user experience.
While Stripe remains one of the biggest online payment services in the world.
Consider subscribing to get simplified case studies delivered straight to your inbox:
Early Announcement
I'm working on deep-dive content to help you become good at work and pass job interviews. But it will be available only to paid subscribers of the newsletter. Also I don't know how long it will take to go live.
The subscription fee will be a lot higher than the current pledge fees after the content is live. So consider pledging now if you need access to deep dives at a lower price.
Thank you for supporting this newsletter. Consider sharing this post with your friends and get rewards. Y’all are the best.
I love your work !
Great to see how they solved an initial problem of retries!
Still, exponential backoff + jitter could create a retry storm if there are calls multiple layers deep. So use it with care! If those multiple layers exist, implementing some retry budget with a token bucket or circuit breakers can help to avoid taking everything down on retries.