Building a Secure Custom Payment Gateway Webhook in PHP

Md Nazmus Shakib
MERN Stack Developer

When operating a SaaS platform, digital agency, or domain registration service, handling automated billing is a critical necessity. While initiating a payment is straightforward, securely confirming that a payment was successfully processed requires a robust background listener—a Webhook.
If a payment webhook is poorly engineered, malicious actors can simulate fake successful payment payloads to your server, granting themselves free domains or premium subscriptions. In this guide, we will engineer a bulletproof PHP payment listener capable of thwarting spoofing, replay attacks, and transaction duplication.
1. The Anatomy of a Webhook & Cryptographic Verification
When a transaction succeeds, the payment provider (e.g., Stripe, Paddle, SSLCommerz) sends an HTTP POST request to your server. To prove that this request genuinely originated from the provider and was not altered in transit, the provider signs the payload using a cryptographic hash (typically HMAC SHA-256) based on a shared secret key.
Your PHP script must capture the raw payload and independently compute this hash. If your computed hash matches the hash provided in the HTTP header, the request is authentic.
Never use standard equality operators (== or ===) to compare cryptographic hashes. Standard operators return false the moment a character doesn't match, allowing attackers to measure the response time and guess the signature byte by byte. Always use PHP's native hash_equals().
2. Preventing Replay Attacks & Implementing Idempotency
A replay attack occurs when a valid webhook payload is intercepted and repeatedly sent to your server. If your script blindly credits the user account for every incoming request, a $10 payment could theoretically add $1,000 to their wallet.
To mitigate this, your database must enforce Idempotency. Every transaction must have a unique identifier provided by the gateway (e.g., trx_id). You must ensure this ID is processed exactly once.
3. Asynchronous Execution for High Scalability
Payment providers typically require your server to respond with a 200 OK within 3 to 5 seconds. If your webhook script triggers heavy processes—such as provisioning a new CPanel account, executing a remote domain registrar API call, or sending PDF invoice emails—your script might time out.
When this happens, the provider assumes the webhook failed and will retry, leading to duplicated provisioning attempts.
The Solution: Your webhook file should only verify the signature, save the raw JSON to a database table or a Message Queue (like Redis or Beanstalkd), and instantly return a 200 OK. A separate background cron job or PHP worker daemon should then pick up the queued payload and execute the heavy API logic asynchronously.
Conclusion
Building a robust billing integration requires a mindset that assumes every incoming HTTP request is inherently hostile until mathematically proven otherwise. By implementing HMAC signature verification, maintaining strict idempotency through unique database constraints, and decoupling heavy provisioning logic into background workers, you can engineer PHP microservices capable of handling millions of dollars in transactions securely and reliably.

Md Nazmus Shakib
MERN Stack Developer
Specializing in the MERN stack and modern frameworks to engineer secure, SEO-optimized, and scalable digital solutions for visionary brands.
More about the author →
