When integrating payment providers such as Yoco or Paystack, it's easy to focus entirely on the checkout experience.
A customer selects a product, makes a payment, gets redirected and sees a success message.
But there's another important part of payment integration that happens behind the scenes: what happens after the payment provider processes the transaction?
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?
These questions led me to build the KALXA Payment Service, a dedicated FastAPI backend designed to handle payment provider webhooks.
The project is part of the broader KALXA ecosystem, which provides digital infrastructure for businesses, restaurants and event organisers.
The service is built with Python, FastAPI and SQLAlchemy, and is deployed on Render.
GitHub: https://github.com/LasieHlongwane/payment-webhooks
Why I Built a Dedicated Payment Service
KALXA already provides digital business profiles, restaurant experiences and online event ticketing.
As the platform grows, payment processing becomes an increasingly important part of its infrastructure.
Rather than keeping every payment integration inside the main application, I decided to build a separate payment service.
The idea is straightforward:
- Keep payment provider integrations independent.
- Centralise webhook handling.
- Verify incoming payment notifications.
- Process payment events through a dedicated backend.
- Make it easier to introduce additional payment providers.
This gives KALXA a foundation for supporting multiple payment integrations without tightly coupling payment logic to the main application.
The Technology Stack
I used the following technologies:
Technology| Purpose
Python| Backend development
FastAPI| REST API and webhook endpoints
SQLAlchemy| Database operations
PostgreSQL| Persistent data storage
Pydantic| Request validation and configuration
HTTPX| HTTP communication
Render| Cloud deployment
Yoco| Initial payment provider integration
- Building the FastAPI Service
The first step was creating a standalone FastAPI application.
The service exposes HTTP endpoints that payment providers can communicate with.
The main Yoco webhook endpoint is:
POST /webhooks/yoco
This endpoint receives payment notifications from Yoco.
Keeping the endpoint separate from KALXA's main application means payment notifications can be handled independently.
The service is publicly accessible through its Render deployment:
https://kalxa-payment-service.onrender.com
- Understanding Webhook Security
A webhook endpoint is publicly accessible.
That means anyone who knows its URL can potentially send an HTTP request to it.
Simply receiving a notification does not mean it should be trusted.
Payment providers use webhook signatures to help applications verify the authenticity of incoming notifications.
The important principle is that the signature must be verified before the application performs any payment-related business action.
For this reason, the service uses environment-based secret configuration.
For the Yoco integration, the webhook signing secret is stored as:
YOCO_WEBHOOK_SECRET
The secret is generated by Yoco when registering a webhook.
It is configured directly in the Render environment rather than being committed to the GitHub repository.
This keeps sensitive credentials separate from application source code.
- Integrating Yoco in Test Mode
Yoco is the first payment provider being integrated into the KALXA Payment Service.
I started with test credentials rather than live payment credentials.
The webhook was registered using Yoco's Register Webhook API.
The registration request contained:
{
"name": "KALXA Payment Service Test",
"url": "https://kalxa-payment-service.onrender.com/webhooks/yoco"
}
After registration, Yoco generated a webhook signing secret.
I added that secret to the Render environment variables as:
YOCO_WEBHOOK_SECRET
The service was then redeployed successfully.
At this stage, the infrastructure was ready for webhook delivery testing.
The next step is to verify successful delivery, signature validation and event processing using Yoco test events.
- Why Webhook Idempotency Matters
One of the most important concepts in payment infrastructure is idempotency.
Payment providers may deliver the same webhook more than once.
For example:
- A customer makes a payment.
- Yoco sends a webhook to your application.
- Your application processes the notification.
- The provider does not receive the expected acknowledgement.
- The provider retries the notification.
Without proper duplicate protection, your application could process the same payment event multiple times.
That could result in duplicate ticket confirmations, incorrect subscription updates or other financial inconsistencies.
The solution is to design webhook processing around unique event identifiers and database-level duplicate protection.
A possible implementation uses a unique database constraint:
class PaymentEvent(Base):
tablename = "payment_events"
__table_args__ = ( UniqueConstraint( "provider", "event_id", name="uq_provider_event" ), ) The database constraint ensures that the same provider event cannot be inserted twice.
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.
- Designing for Reliable Event Processing
Payment webhooks should be treated as events rather than ordinary HTTP requests.
A reliable implementation needs to distinguish between:
- Successfully received events.
- Duplicate events.
- Invalid signatures.
- Malformed requests.
- Temporary database or processing failures.
The HTTP response matters because payment providers use it to determine whether a notification was successfully delivered.
A typical webhook response strategy looks like this:
Situation| HTTP Status
Successfully processed event| 200
Duplicate event| 200
Invalid signature| 401
Malformed request| 400
Temporary processing failure| 503
The objective is to acknowledge events that have already been safely handled while allowing providers to retry temporary failures.
This is an important reliability requirement for the next stage of development.
- Deploying the Payment Service on Render
After developing the FastAPI application, I deployed it to Render.
The deployment provides a publicly accessible HTTPS endpoint that Yoco can use to deliver webhook notifications.
The deployment process involved:
- Creating the GitHub repository.
- Building the FastAPI application.
- Configuring the application dependencies.
- Connecting the repository to Render.
- Configuring environment variables.
- Deploying the application.
- Registering the Yoco test webhook.
- Adding the generated webhook secret to Render.
- Confirming that the service successfully redeployed and showed Live.
The service is now running independently of the main KALXA application.
- What's Next?
The initial infrastructure and Yoco test webhook configuration are complete.
There are several important development milestones ahead.
Webhook verification testing
Confirm that Yoco can successfully deliver test notifications and that the service correctly verifies their signatures.
Idempotent event processing
Implement and test database-level duplicate protection to ensure that repeated notifications cannot trigger duplicate business actions.
Payment record management
Connect verified payment events to persistent payment records.
KALXA integration
Integrate the payment service with KALXA's ticketing and business platform so that verified payment events can trigger the appropriate business actions.
Paystack integration
Introduce Paystack as an additional payment provider, extending the service beyond its initial Yoco integration.
Production readiness
Expand automated testing, error handling, monitoring and security controls before enabling live payment processing.
What I've Learned
Building this service has reinforced several important backend engineering principles.
- Payment notifications must be trusted, not assumed to be genuine.
A public endpoint needs proper verification before accepting payment-related events.
- Payment infrastructure should be separated from business applications.
A dedicated payment service creates a cleaner architecture and makes future integrations easier to maintain.
- Webhook reliability requires more than receiving HTTP requests.
Duplicate handling, retries, database consistency and observability are essential parts of a dependable payment system.
- Test environments are important.
Building and testing integrations before introducing live payment credentials helps identify problems without risking real transactions.
Conclusion
The KALXA Payment Service is an important step towards building more independent and scalable infrastructure for the KALXA ecosystem.
What started as a requirement to handle payment notifications has become a dedicated backend project focused on payment integration, security and reliability.
The initial FastAPI service is deployed, the Yoco test webhook is registered, and the webhook signing secret is configured.
The next challenge is completing delivery verification, reliable event processing and integration with KALXA's existing business workflows.
I'm continuing to build this service incrementally, with the goal of making payment infrastructure a reusable part of the KALXA platform.
I'm Lasie Hlongwane, a Python backend developer building APIs, payment integrations and digital infrastructure through KALXA.
Portfolio: https://lasiehlongwane.github.io/PortfolioHL/
GitHub: https://github.com/LasieHlongwane
KALXA: https://ticket.kalxa.co.za/
LinkedIn: (account restricted currently)https://www.linkedin.com/in/lasie-hlongwane-3b033228b/