<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: LaC Hlongwane</title>
    <description>The latest articles on DEV Community by LaC Hlongwane (@lac_hlongwane_23f28386523).</description>
    <link>https://dev.to/lac_hlongwane_23f28386523</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2652584%2F4c98dc72-6fb1-459e-8e9a-92d631cb1440.jpg</url>
      <title>DEV Community: LaC Hlongwane</title>
      <link>https://dev.to/lac_hlongwane_23f28386523</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lac_hlongwane_23f28386523"/>
    <language>en</language>
    <item>
      <title>How I Built a Payment Webhook Service with FastAPI for KALXA</title>
      <dc:creator>LaC Hlongwane</dc:creator>
      <pubDate>Mon, 02 Nov 2026 08:28:48 +0000</pubDate>
      <link>https://dev.to/lac_hlongwane_23f28386523/how-i-built-a-payment-webhook-service-with-fastapi-for-kalxa-13jc</link>
      <guid>https://dev.to/lac_hlongwane_23f28386523/how-i-built-a-payment-webhook-service-with-fastapi-for-kalxa-13jc</guid>
      <description>&lt;p&gt;When integrating payment providers such as Yoco or Paystack, it's easy to focus entirely on the checkout experience.&lt;/p&gt;

&lt;p&gt;A customer selects a product, makes a payment, gets redirected and sees a success message.&lt;/p&gt;

&lt;p&gt;But there's another important part of payment integration that happens behind the scenes: what happens after the payment provider processes the transaction?&lt;/p&gt;

&lt;p&gt;How does your application know that a payment was successful? How do you verify that the notification actually came from the provider? And how do you prevent payment events from being processed incorrectly?&lt;/p&gt;

&lt;p&gt;These questions led me to build the KALXA Payment Service, a dedicated FastAPI backend designed to handle payment provider webhooks.&lt;/p&gt;

&lt;p&gt;The project is part of the broader KALXA ecosystem, which provides digital infrastructure for businesses, restaurants and event organisers.&lt;/p&gt;

&lt;p&gt;The service is built with Python, FastAPI and SQLAlchemy, and is deployed on Render.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/LasieHlongwane/payment-webhooks" rel="noopener noreferrer"&gt;https://github.com/LasieHlongwane/payment-webhooks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why I Built a Dedicated Payment Service&lt;/p&gt;

&lt;p&gt;KALXA already provides digital business profiles, restaurant experiences and online event ticketing.&lt;/p&gt;

&lt;p&gt;As the platform grows, payment processing becomes an increasingly important part of its infrastructure.&lt;/p&gt;

&lt;p&gt;Rather than keeping every payment integration inside the main application, I decided to build a separate payment service.&lt;/p&gt;

&lt;p&gt;The idea is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep payment provider integrations independent.&lt;/li&gt;
&lt;li&gt;Centralise webhook handling.&lt;/li&gt;
&lt;li&gt;Verify incoming payment notifications.&lt;/li&gt;
&lt;li&gt;Process payment events through a dedicated backend.&lt;/li&gt;
&lt;li&gt;Make it easier to introduce additional payment providers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives KALXA a foundation for supporting multiple payment integrations without tightly coupling payment logic to the main application.&lt;/p&gt;

&lt;p&gt;The Technology Stack&lt;/p&gt;

&lt;p&gt;I used the following technologies:&lt;/p&gt;

&lt;p&gt;Technology| Purpose&lt;br&gt;
Python| Backend development&lt;br&gt;
FastAPI| REST API and webhook endpoints&lt;br&gt;
SQLAlchemy| Database operations&lt;br&gt;
PostgreSQL| Persistent data storage&lt;br&gt;
Pydantic| Request validation and configuration&lt;br&gt;
HTTPX| HTTP communication&lt;br&gt;
Render| Cloud deployment&lt;br&gt;
Yoco| Initial payment provider integration&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Building the FastAPI Service&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step was creating a standalone FastAPI application.&lt;/p&gt;

&lt;p&gt;The service exposes HTTP endpoints that payment providers can communicate with.&lt;/p&gt;

&lt;p&gt;The main Yoco webhook endpoint is:&lt;/p&gt;

&lt;p&gt;POST /webhooks/yoco&lt;/p&gt;

&lt;p&gt;This endpoint receives payment notifications from Yoco.&lt;/p&gt;

&lt;p&gt;Keeping the endpoint separate from KALXA's main application means payment notifications can be handled independently.&lt;/p&gt;

&lt;p&gt;The service is publicly accessible through its Render deployment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kalxa-payment-service.onrender.com" rel="noopener noreferrer"&gt;https://kalxa-payment-service.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understanding Webhook Security&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A webhook endpoint is publicly accessible.&lt;/p&gt;

&lt;p&gt;That means anyone who knows its URL can potentially send an HTTP request to it.&lt;/p&gt;

&lt;p&gt;Simply receiving a notification does not mean it should be trusted.&lt;/p&gt;

&lt;p&gt;Payment providers use webhook signatures to help applications verify the authenticity of incoming notifications.&lt;/p&gt;

&lt;p&gt;The important principle is that the signature must be verified before the application performs any payment-related business action.&lt;/p&gt;

&lt;p&gt;For this reason, the service uses environment-based secret configuration.&lt;/p&gt;

&lt;p&gt;For the Yoco integration, the webhook signing secret is stored as:&lt;/p&gt;

&lt;p&gt;YOCO_WEBHOOK_SECRET&lt;/p&gt;

&lt;p&gt;The secret is generated by Yoco when registering a webhook.&lt;/p&gt;

&lt;p&gt;It is configured directly in the Render environment rather than being committed to the GitHub repository.&lt;/p&gt;

&lt;p&gt;This keeps sensitive credentials separate from application source code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Integrating Yoco in Test Mode&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yoco is the first payment provider being integrated into the KALXA Payment Service.&lt;/p&gt;

&lt;p&gt;I started with test credentials rather than live payment credentials.&lt;/p&gt;

&lt;p&gt;The webhook was registered using Yoco's Register Webhook API.&lt;/p&gt;

&lt;p&gt;The registration request contained:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "name": "KALXA Payment Service Test",&lt;br&gt;
  "url": "&lt;a href="https://kalxa-payment-service.onrender.com/webhooks/yoco" rel="noopener noreferrer"&gt;https://kalxa-payment-service.onrender.com/webhooks/yoco&lt;/a&gt;"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;After registration, Yoco generated a webhook signing secret.&lt;/p&gt;

&lt;p&gt;I added that secret to the Render environment variables as:&lt;/p&gt;

&lt;p&gt;YOCO_WEBHOOK_SECRET&lt;/p&gt;

&lt;p&gt;The service was then redeployed successfully.&lt;/p&gt;

&lt;p&gt;At this stage, the infrastructure was ready for webhook delivery testing.&lt;/p&gt;

&lt;p&gt;The next step is to verify successful delivery, signature validation and event processing using Yoco test events.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why Webhook Idempotency Matters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the most important concepts in payment infrastructure is idempotency.&lt;/p&gt;

&lt;p&gt;Payment providers may deliver the same webhook more than once.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer makes a payment.&lt;/li&gt;
&lt;li&gt;Yoco sends a webhook to your application.&lt;/li&gt;
&lt;li&gt;Your application processes the notification.&lt;/li&gt;
&lt;li&gt;The provider does not receive the expected acknowledgement.&lt;/li&gt;
&lt;li&gt;The provider retries the notification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without proper duplicate protection, your application could process the same payment event multiple times.&lt;/p&gt;

&lt;p&gt;That could result in duplicate ticket confirmations, incorrect subscription updates or other financial inconsistencies.&lt;/p&gt;

&lt;p&gt;The solution is to design webhook processing around unique event identifiers and database-level duplicate protection.&lt;/p&gt;

&lt;p&gt;A possible implementation uses a unique database constraint:&lt;/p&gt;

&lt;p&gt;class PaymentEvent(Base):&lt;br&gt;
    &lt;strong&gt;tablename&lt;/strong&gt; = "payment_events"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;__table_args__ = (
    UniqueConstraint(
        "provider",
        "event_id",
        name="uq_provider_event"
    ),
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The database constraint ensures that the same provider event cannot be inserted twice.&lt;/p&gt;

&lt;p&gt;This is an important part of the service's reliability roadmap and will be verified through automated tests before being treated as a completed production guarantee.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Designing for Reliable Event Processing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Payment webhooks should be treated as events rather than ordinary HTTP requests.&lt;/p&gt;

&lt;p&gt;A reliable implementation needs to distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Successfully received events.&lt;/li&gt;
&lt;li&gt;Duplicate events.&lt;/li&gt;
&lt;li&gt;Invalid signatures.&lt;/li&gt;
&lt;li&gt;Malformed requests.&lt;/li&gt;
&lt;li&gt;Temporary database or processing failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The HTTP response matters because payment providers use it to determine whether a notification was successfully delivered.&lt;/p&gt;

&lt;p&gt;A typical webhook response strategy looks like this:&lt;/p&gt;

&lt;p&gt;Situation| HTTP Status&lt;br&gt;
Successfully processed event| 200&lt;br&gt;
Duplicate event| 200&lt;br&gt;
Invalid signature| 401&lt;br&gt;
Malformed request| 400&lt;br&gt;
Temporary processing failure| 503&lt;/p&gt;

&lt;p&gt;The objective is to acknowledge events that have already been safely handled while allowing providers to retry temporary failures.&lt;/p&gt;

&lt;p&gt;This is an important reliability requirement for the next stage of development.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploying the Payment Service on Render&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After developing the FastAPI application, I deployed it to Render.&lt;/p&gt;

&lt;p&gt;The deployment provides a publicly accessible HTTPS endpoint that Yoco can use to deliver webhook notifications.&lt;/p&gt;

&lt;p&gt;The deployment process involved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating the GitHub repository.&lt;/li&gt;
&lt;li&gt;Building the FastAPI application.&lt;/li&gt;
&lt;li&gt;Configuring the application dependencies.&lt;/li&gt;
&lt;li&gt;Connecting the repository to Render.&lt;/li&gt;
&lt;li&gt;Configuring environment variables.&lt;/li&gt;
&lt;li&gt;Deploying the application.&lt;/li&gt;
&lt;li&gt;Registering the Yoco test webhook.&lt;/li&gt;
&lt;li&gt;Adding the generated webhook secret to Render.&lt;/li&gt;
&lt;li&gt;Confirming that the service successfully redeployed and showed Live.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The service is now running independently of the main KALXA application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What's Next?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The initial infrastructure and Yoco test webhook configuration are complete.&lt;/p&gt;

&lt;p&gt;There are several important development milestones ahead.&lt;/p&gt;

&lt;p&gt;Webhook verification testing&lt;/p&gt;

&lt;p&gt;Confirm that Yoco can successfully deliver test notifications and that the service correctly verifies their signatures.&lt;/p&gt;

&lt;p&gt;Idempotent event processing&lt;/p&gt;

&lt;p&gt;Implement and test database-level duplicate protection to ensure that repeated notifications cannot trigger duplicate business actions.&lt;/p&gt;

&lt;p&gt;Payment record management&lt;/p&gt;

&lt;p&gt;Connect verified payment events to persistent payment records.&lt;/p&gt;

&lt;p&gt;KALXA integration&lt;/p&gt;

&lt;p&gt;Integrate the payment service with KALXA's ticketing and business platform so that verified payment events can trigger the appropriate business actions.&lt;/p&gt;

&lt;p&gt;Paystack integration&lt;/p&gt;

&lt;p&gt;Introduce Paystack as an additional payment provider, extending the service beyond its initial Yoco integration.&lt;/p&gt;

&lt;p&gt;Production readiness&lt;/p&gt;

&lt;p&gt;Expand automated testing, error handling, monitoring and security controls before enabling live payment processing.&lt;/p&gt;

&lt;p&gt;What I've Learned&lt;/p&gt;

&lt;p&gt;Building this service has reinforced several important backend engineering principles.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Payment notifications must be trusted, not assumed to be genuine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A public endpoint needs proper verification before accepting payment-related events.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Payment infrastructure should be separated from business applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A dedicated payment service creates a cleaner architecture and makes future integrations easier to maintain.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Webhook reliability requires more than receiving HTTP requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Duplicate handling, retries, database consistency and observability are essential parts of a dependable payment system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Test environments are important.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Building and testing integrations before introducing live payment credentials helps identify problems without risking real transactions.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;The KALXA Payment Service is an important step towards building more independent and scalable infrastructure for the KALXA ecosystem.&lt;/p&gt;

&lt;p&gt;What started as a requirement to handle payment notifications has become a dedicated backend project focused on payment integration, security and reliability.&lt;/p&gt;

&lt;p&gt;The initial FastAPI service is deployed, the Yoco test webhook is registered, and the webhook signing secret is configured.&lt;/p&gt;

&lt;p&gt;The next challenge is completing delivery verification, reliable event processing and integration with KALXA's existing business workflows.&lt;/p&gt;

&lt;p&gt;I'm continuing to build this service incrementally, with the goal of making payment infrastructure a reusable part of the KALXA platform.&lt;/p&gt;




&lt;p&gt;I'm Lasie Hlongwane, a Python backend developer building APIs, payment integrations and digital infrastructure through KALXA.&lt;/p&gt;

&lt;p&gt;Portfolio: &lt;a href="https://lasiehlongwane.github.io/PortfolioHL/" rel="noopener noreferrer"&gt;https://lasiehlongwane.github.io/PortfolioHL/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/LasieHlongwane" rel="noopener noreferrer"&gt;https://github.com/LasieHlongwane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;KALXA: &lt;a href="https://ticket.kalxa.co.za/" rel="noopener noreferrer"&gt;https://ticket.kalxa.co.za/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LinkedIn: (account restricted currently)&lt;a href="https://www.linkedin.com/in/lasie-hlongwane-3b033228b/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/lasie-hlongwane-3b033228b/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
    </item>
  </channel>
</rss>
