self-hosted payment gateway software

Tune a PHP payment server for reliable webhook work

A practical Nginx, MySQL and worker pattern keeps OwnPay webhook handling responsive without treating retries as exceptional.

By Aria Channing·October 21, 2026·4 min read
What matters here
  1. A webhook receiver should persist a verified event before returning success, then leave slow work to a worker.
  2. Keep MySQL transactions short; test isolation changes against the payment application rather than applying them globally.
  3. Tune Nginx and PHP-FPM from measured concurrency and latency, not copied server-size recipes.

A payment webhook can arrive while a customer is checking out, while a gateway is retrying an earlier notification, or during a burst of settlement updates. The receiver has two jobs: verify the signed request and make its work durable. Email, fulfillment and other slow tasks belong somewhere else.

For a self-hosted payment gateway deployment, that division matters as much as raw server capacity. OwnPay is open-source payment gateway software for PHP servers. It provides cryptographically signed webhooks, REST API endpoints and a double-entry bookkeeping ledger. The guidance below describes an operational pattern around those capabilities; it does not assume OwnPay supplies a particular queue backend or database configuration.

Separate receipt from processing

Consider a merchant running two storefronts through OwnPay’s multi-brand architecture. A payment update may need to change an order, notify fulfillment and send a customer message. If the webhook request performs all three tasks before responding, a slow mail service or fulfillment API can hold up the gateway callback. A timeout may prompt a retry, even when part of the work already happened.

Instead, have the receiving application verify the webhook signature according to its documentation, record the verified request in durable storage, and return success only after that write commits. A background worker can then process the stored event. Keep the original request body and relevant verification details long enough to diagnose failures, subject to the merchant’s retention rules.

Design processing to tolerate duplicate delivery. Use a stable event identifier for deduplication if the webhook provides one, and confirm its meaning in the integration documentation. If it does not, define a safe application-level deduplication key rather than assuming every delivery is unique. A worker should be able to retry a failed task without creating a second shipment or posting a second accounting action.

For background on connecting a custom PHP application and verifying OwnPay callbacks, see the Merchant API integration guide.

Set Nginx and PHP-FPM from observed load

In a PHP payment gateway server setup using Nginx and PHP-FPM, size the request path around short callback handling, not around the total time needed to fulfill an order. Check Nginx’s worker connections against expected concurrent requests and operating-system file-descriptor limits. Then confirm PHP-FPM has enough available workers for the measured peak without allowing PHP processes to exhaust memory.

Track request duration, upstream timeouts, PHP-FPM busy processes and the rate of non-success responses. Raise limits only when those measurements show a bottleneck. Increasing worker counts without checking memory can make an overloaded host less stable. Set FastCGI timeouts to accommodate normal application work, but do not use a long timeout to disguise slow external calls inside the webhook request.

Keep the receiver’s response path small. Avoid doing network calls to fulfillment, email or analytics services before acknowledging a durably stored event. If the receiver cannot write the event, it should not report success; otherwise the sender may stop retrying work the application has lost. Test this failure path, including a full disk or unavailable database, before launch.

Keep MySQL transactions narrow

If the deployment uses MySQL, treat isolation as an application-level decision. Start with the isolation level required by the payment application and its documented installation procedure. Do not change the server-wide default just because a benchmark suggests it. Isolation changes can affect concurrent reads, locking and ledger consistency.

Keep transactions short: write the event record, update the related application state and commit. Do not hold a database transaction open while waiting on an HTTP service. Monitor lock waits, deadlocks and slow queries under a realistic webhook burst. Add indexes only after checking query plans and the workload; extra indexes also increase write cost.

A deadlock is a normal concurrency failure to handle, not a reason to repeat every side effect blindly. Retry the affected database transaction with a bounded policy, and keep external actions idempotent. OwnPay includes double-entry bookkeeping functionality, so operators should not bypass its accounting paths by writing directly to ledger tables. Use the application’s supported interfaces and verify accounting results through the appropriate application workflow.

Run workers as supervised processes

For webhook queue worker setup, use a durable queue the application can support: a database-backed job table or a separate broker are common patterns, but the right choice depends on the surrounding application and operational skills. The queue needs persistence across process restarts, visibility into failed jobs and a defined retry limit. A queue that exists only in PHP process memory does not protect work during a restart.

Run workers as supervised long-lived processes, with a controlled number of workers and a restart policy. Set limits on job attempts and execution time. Send repeatedly failing jobs to a reviewable failure state rather than retrying forever. Watch queue depth and oldest-job age; a growing backlog is an earlier warning than a customer complaint. Keep worker concurrency within the database and downstream services’ capacity.

Finally, test the whole path: send a valid event, replay it, interrupt the worker, and make the database unavailable during receipt. Confirm that valid work is eventually processed once in business terms, duplicates do not repeat side effects, and failed receipt is visible. OwnPay’s 0% platform transaction cut does not remove the operator’s infrastructure work. The payoff of self-hosting is control; dependable webhook handling is part of the cost of exercising it.

More from OwnPay News