When building modern digital platforms, handling payments reliably is one of the most critical challenges. While payment gateways provide robust APIs, real-world scenarios often introduce complexities. One common issue is when a payment initially fails, but after retries from the provider's side, it eventually succeeds. This can lead to confusion for both users and businesses:
This problem stems from how asynchronous payment systems work. Payment providers may retry transactions on behalf of the user, and if the retry succeeds, they later send a notification (webhook/event) to the merchant. If the merchant system is not designed to handle these late success events, users end up being charged without getting their product.
This is exactly where Event-Driven Architecture (EDA) becomes a game-changer.
Event-Driven Architecture is a design approach where services communicate through events rather than direct API calls. Instead of polling or waiting for responses, systems emit events when something happens (e.g., "Payment Succeeded"), and other systems react to those events.
payment_succeeded
webhook).Let's map the payment retry scenario into an event-driven workflow:
payment_failed
.payment_succeeded
event via webhook.Your system has an event listener (webhook endpoint) subscribed to provider events.
When it receives payment_succeeded
, it:
Let's say you're building this with Node.js + Kafka (or AWS SQS):
payment_succeeded
and updates your order DB.This decoupled architecture ensures that even if one service fails, others can still process events independently.
1+-----------------------+2| USER/CLIENT |3+-----------------------+4 |5 v6+-------------------------------------------------+7| UI & BACKEND (INITIAL PAYMENT CALL) |8|-------------------------------------------------|9| - User clicks "Pay" |10| - UI shows "Payment Failed" |11| - User sees failure message |12+-------------------------------------------------+13 | (Request)14 v15+-----------------------------------------------------------------+16| PAYMENT GATEWAY |17|-----------------------------------------------------------------|18| - Receives initial payment request |19| - First attempt FAILS |20| - --------------------------------- |21| - **INTERNAL RETRY MECHANISM** |22| - Second attempt SUCCEEDS a few seconds later |23| - --------------------------------- |24| - **ASYNC WEBHOOK** sends `payment_succeeded` event |25+-----------------------------------------------------------------+26 |27 | (Event: `payment_succeeded`)28 v29+-----------------------------------------------------------------+30| EVENT BROKER (Kafka, RabbitMQ) |31|-----------------------------------------------------------------|32| - Receives the webhook event from the Payment Gateway |33| - Puts the event on a dedicated queue |34| - Decouples the sender and receiver |35+-----------------------------------------------------------------+36 |37 | (Event: `payment_succeeded`)38 v39+-----------------------------------------------------------------+40| PAYMENT SERVICE (Consumer) |41|-----------------------------------------------------------------|42| - **Listens** for `payment_succeeded` events |43| - Updates the Order in the DB (mark payment as successful) |44| - Triggers order fulfillment |45+-----------------------------------------------------------------+46 | |47 | (Event: `order_fulfilled`) |48 v v49+---------------------+ +-----------------------------------+50| ORDER FULFILLMENT | | NOTIFICATION SERVICE |51| SERVICE | | (Consumer) |52|---------------------| +-----------------------------------+53| - Listens for | | - Listens for payment events |54| fulfillment events| | - Sends confirmation email/SMS |55| - Delivers product | | to the user |56| or subscription | +-----------------------------------+57+---------------------+58 |59 | (Event: `order_fulfilled`)60 v61+------------------------------------+62| ANALYTICS SERVICE (Consumer) |63|------------------------------------|64| - Listens for all payment events |65| - Logs transactions for reporting |66+------------------------------------+
Payment retries and delayed confirmations are inevitable in real-world payment systems. Instead of treating them as errors, we should design our systems to handle them gracefully.
With Event-Driven Architecture, we:
If you're building any system that involves payments, subscriptions, or external APIs with retries, adopting EDA can save you from frustrated users and endless support tickets.
✅ Takeaway: In payments, the first response is not always the final truth. Events are.