<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Usman Tahir Qureshi — SRE Notes]]></title><description><![CDATA[Reliability, security, and disaster recovery for financial infrastructure — notes from an SRE in fintech.]]></description><link>https://usmanqureshi.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Usman Tahir Qureshi — SRE Notes</title><link>https://usmanqureshi.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 30 Aug 2026 19:08:09 GMT</lastBuildDate><atom:link href="https://usmanqureshi.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why Payment Systems Fail Differently Than Everything Else]]></title><description><![CDATA[I got paged once for something that, on paper, wasn't even a bug. Customers were completing checkout, and right after payment, the screen went white. No error message. No confirmation. Just blank.
Sup]]></description><link>https://usmanqureshi.hashnode.dev/why-payment-systems-fail-differently-than-everything-else</link><guid isPermaLink="true">https://usmanqureshi.hashnode.dev/why-payment-systems-fail-differently-than-everything-else</guid><category><![CDATA[fintech]]></category><category><![CDATA[payments]]></category><category><![CDATA[AWS]]></category><category><![CDATA[observability]]></category><dc:creator><![CDATA[Usman Tahir Qureshi]]></dc:creator><pubDate>Wed, 26 Aug 2026 23:54:42 GMT</pubDate><content:encoded><![CDATA[<p>I got paged once for something that, on paper, wasn't even a bug. Customers were completing checkout, and right after payment, the screen went white. No error message. No confirmation. Just blank.</p>
<p>Support tickets started coming in fast, and they all asked the same question: did my payment actually go through?</p>
<p>That question is the whole reason payment engineering is a different discipline from most software engineering. In almost any other system, a broken screen after an action is an annoyance. Refresh the page, try again, worst case you redo a form. In a payment system, "try again" can mean charging someone twice. And "wait and see" can mean a customer walks away from checkout believing they lost money, or believing they never paid when they did.</p>
<p>I pulled up Splunk and started tracing individual customer sessions against the payment processor's response logs. Some had gone through cleanly on the backend. The charge succeeded, the confirmation just never rendered. Others hadn't gone through at all. From the customer's side, both looked identical: a white screen and silence. The bug itself, once found, was a rendering failure on the confirmation step. But the incident wasn't really about that bug. It was about how much operational weight a single broken screen carried, because the system had no independent way to tell a customer what actually happened to their money.</p>
<p>That's the pattern worth understanding, because it shows up constantly in payment systems, in different shapes. The hard part usually isn't the happy path. It's every ambiguous state where you don't yet know what happened, and every second a customer spends not knowing either.</p>
<h4>Idempotency: how to make retries safe instead of dangerous</h4>
<p>In most systems, a timeout means "it didn't work, move on." In a payment system, a timeout, or a crashed confirmation screen like mine, means "I don't know what happened, and I have to find out before I do anything else."</p>
<pre><code class="language-plaintext">POST /charges
{
  "amount": 4999,
  "currency": "usd",
  "customer": "cus_abc123"
}

response: connection timeout after 30s
</code></pre>
<p>Did the downstream processor receive it? Did it charge the card and the response just never make it back? You cannot safely retry this request as it is written. A naive retry can produce two charges for one purchase.</p>
<p>The fix is an idempotency key: a client-generated identifier attached to the request that represents "this specific attempt to charge this specific thing."</p>
<pre><code class="language-plaintext">POST /charges
Idempotency-Key: order_9f21c-attempt_1
{
  "amount": 4999,
  "currency": "usd",
  "customer": "cus_abc123"
}
</code></pre>
<p>The server stores the key alongside the result of the first attempt. If the same key comes in again, whether from a network retry, a client bug, or a customer double-clicking a button, the server returns the original stored result instead of processing a new charge. The mechanics that make this work reliably:</p>
<ul>
<li><p>The key needs to be generated once per logical attempt, on the client or at the earliest point in your system, and reused on every retry of that same attempt. Generating a new key on every retry defeats the entire purpose.</p>
</li>
<li><p>The server needs to store the key and result atomically with the charge itself, typically in the same database transaction, so there is no window where a charge exists without its key recorded.</p>
</li>
<li><p>Keys need an expiration window (commonly 24 hours), long enough to cover realistic retry scenarios, short enough that the storage doesn't grow unbounded.</p>
</li>
</ul>
<p>This is table stakes for any payment API, and it is the first thing to verify exists and is actually enforced correctly, not just present in the code, whenever you inherit a payment system.</p>
<h4>Why my incident was a visibility problem, not a retry problem</h4>
<p>The charges themselves were fine in my case. The backend did exactly what it should have done. The failure was that nothing downstream, not the UI, not an event, not a status endpoint support could check, could confirm quickly whether a given payment succeeded, for either the customer or for me at 2am with a pager going off.</p>
<p>The fix was not just patching the rendering bug. It was building a payment status lookup that is independent of the checkout flow itself: an endpoint or dashboard that answers "what happened to transaction X" by querying the source of truth directly, not by inferring it from whatever the frontend last displayed. Concretely, that means:</p>
<ul>
<li><p>A <code>GET /charges/{id}/status</code> endpoint that customers or support staff can query independently of the checkout UI.</p>
</li>
<li><p>Logging structured, queryable events at each state transition (initiated, authorized, captured, failed) rather than only unstructured text logs, so an engineer can search by customer or transaction ID instead of reading through raw log lines during an incident.</p>
</li>
<li><p>A support-facing tool that surfaces this status without requiring an engineer and a Splunk query every time a customer asks.</p>
</li>
</ul>
<h4>Reconciliation: why the system should not fully trust itself</h4>
<p>In a typical web app, a failed transaction rolls back and the world is unchanged. In a payment flow, "rollback" can mean issuing a refund, reversing a ledger entry, or notifying a third party, each with its own failure modes, its own delays, and its own audit trail requirements. You cannot roll back money that has already moved between two parties with a single database statement.</p>
<p>This is why reconciliation exists as its own discipline in payments infrastructure. On a schedule, typically daily, a job compares what your system recorded against what the payment processor, bank, or ledger actually shows, and flags the differences. A basic version looks like this:</p>
<ol>
<li><p>Pull your internal transaction log for the period.</p>
</li>
<li><p>Pull the processor's settlement report for the same period.</p>
</li>
<li><p>Match records by transaction ID or idempotency key.</p>
</li>
<li><p>Flag anything present in one dataset but not the other, and anything where the amounts do not match.</p>
</li>
</ol>
<p>If reconciliation and a clear status lookup had not existed during my incident, I would have had no fast way to tell which customers actually needed a manual fix and which did not. Reconciliation is what lets a team trust its own numbers instead of assuming the last successful deploy means everything downstream agrees.</p>
<h4>Why the blast radius is different</h4>
<p>A bug in a recommendation engine shows someone the wrong product. A bug in a payment confirmation flow makes customers doubt whether their money is safe with you, and that doubt spreads faster than the bug itself does. The engineering mistake and the trust consequence are far more tightly coupled here than in most software domains.</p>
<p>That coupling is why payment infrastructure tends to look over-engineered relative to its apparent complexity, and why that is usually the right call. The idempotency key, the reconciliation job, the status endpoint nobody thinks about until the day the confirmation screen goes white: none of it looks necessary until the one incident where it is the only reason a bad afternoon did not become a very bad quarter.</p>
<h4>What to check if you are new to a payment system</h4>
<p>If you are inheriting or building payment infrastructure, these are the concrete things worth verifying exist, not just assuming they do:</p>
<ol>
<li><p>Every write operation that moves money has an enforced idempotency key, checked at the database layer, not only in application code.</p>
</li>
<li><p>There is a status lookup independent of any single client flow, so "did this work" never depends on one UI rendering correctly.</p>
</li>
<li><p>A reconciliation job runs on a defined schedule and someone is actually reviewing its output, not just letting it run silently.</p>
</li>
<li><p>State transitions are logged as structured, searchable events, so an incident does not require manually tracing raw logs the way mine did.</p>
</li>
</ol>
<h4>Where this series is going</h4>
<p>This is the first in a series on building and operating reliable, secure payment infrastructure: from the foundational concepts above, into compliance as code and security observability, disaster recovery for financial systems, and eventually where ML and AI genuinely help, and where they do not, in keeping these systems trustworthy.</p>
<p>If you have worked in payments, incident response, or fintech infrastructure, what is the failure mode that took you longest to really understand? Drop it in the comments.</p>
<p>#Fintech #DevOps #SRE #PaymentSystems #Reliability #IncidentResponse #CloudEngineering</p>
]]></content:encoded></item></channel></rss>