BookStack Does Not Process Payments, and Why That Matters

Before troubleshooting anything, it is worth setting expectations clearly: BookStack is a self-hosted wiki and documentation platform. It has no shopping cart, no checkout page, no payment gateway integration, and no concept of a transaction. There is no Stripe, PayPal, or PCI workflow anywhere in the application, and there never has been. If you arrived here expecting to fix a broken checkout or a failed payment callback, that workflow simply does not exist in this software. Trying to force a payment plugin into it is not possible because BookStack has no third-party plugin marketplace and no gateway abstraction layer.

What BookStack does have is the mechanism people frequently confuse with payment callbacks: outbound webhooks and a REST API. These are the two features that generate the same class of problems you would see with a payment integration, namely cURL failures, TLS handshake errors, credential rejection, and silent delivery failures. When a page is created, updated, or deleted, BookStack can fire an HTTP POST to an external endpoint. That endpoint might be an automation service, a billing system running elsewhere, a chat notification, or your own middleware that ultimately touches money. The confusion is understandable, because the failure symptoms are identical to a broken payment webhook. The fix, however, lives entirely in how BookStack makes outbound HTTP calls and how it authenticates API requests.

Webhooks in BookStack are configured under Settings → Webhooks in the admin area, reached at /settings/webhooks. Each webhook has an endpoint URL, a list of trigger events, and a timeout. When an event fires, BookStack uses the server's PHP cURL stack to deliver the payload. On a shared LiteSpeed and CloudLinux environment, that outbound call is where things break, and because you have no root access, every fix has to happen through cPanel, DirectAdmin, .user.ini, or the application settings themselves.

Diagnosing Webhook Delivery and Timeout Failures

The first place to look is not the receiving server but BookStack's own logs. On shared hosting your application writes to a PHP error_log, and BookStack additionally logs to storage/logs/laravel.log under your document root, for example /home/username/public_html/storage/logs/laravel.log. Open it through the cPanel or DirectAdmin File Manager and search for entries containing webhook or GuzzleHttp. BookStack uses the Guzzle HTTP client, so a failed delivery typically records a connection exception with a specific reason: connection timed out, could not resolve host, or an SSL certificate problem. That single line tells you which of the following categories you are dealing with, and saves hours of guessing.

Timeouts are the most common complaint. BookStack's default webhook timeout is short, and a slow receiving endpoint will exceed it. You can raise the per-webhook timeout in the webhook edit screen at /settings/webhooks, but the receiving side must still respond quickly because the delivery happens inline with the page save. If the endpoint takes several seconds, users experience a lag when saving pages. The correct pattern is to point the webhook at a fast-acknowledging receiver that queues the work, rather than at a slow synchronous script. If deliveries fail intermittently, check whether your account is hitting the CloudLinux LVE process or memory limits during the save; those limits are visible in cPanel under Resource Usage → Current Usage. A save that gets killed mid-request never fires its webhook, and the log will show an abrupt termination rather than a clean HTTP error.

DNS resolution failures ("could not resolve host") usually mean the endpoint hostname is internal or the outbound call is being blocked. On managed shared hosting, some outbound ports are restricted. Standard HTTPS on port 443 is normally open, but non-standard ports frequently are not. Confirm your webhook URL uses https:// on port 443 rather than a custom port. If you genuinely need an unusual outbound port opened, that is a request for our support team, because port policy is a server-level control you cannot change from your account.

Fixing cURL and TLS Handshake Errors

The error you will dread most is the TLS one, logged as cURL error 60: SSL certificate problem: unable to get local issuer certificate. This means the PHP cURL stack cannot validate the receiving server's certificate against a trusted CA bundle. It almost always follows a PHP version change made in cPanel's MultiPHP Manager or the CloudLinux PHP Selector, where the new PHP build points at a missing or stale cacert.pem. Do not attempt to disable certificate verification as a fix; that turns a documentation-security problem into a real one, because your webhook payloads could then be delivered to an impostor.

The supported approach on shared hosting is to supply a current CA bundle and reference it through your account-level PHP configuration. Download the current Mozilla CA bundle, upload it via File Manager to a path inside your home directory such as /home/username/cacert.pem, and set it in a .user.ini file placed in your BookStack document root:

; /home/username/public_html/.user.ini
curl.cainfo = "/home/username/cacert.pem"
openssl.cafile = "/home/username/cacert.pem"

Because .user.ini is read per directory and cached, allow a few minutes or recycle the PHP application from cPanel before testing. After the change, edit the webhook and save it, then use the receiving endpoint's own logs or a request-inspection service to confirm the POST arrives. If Guzzle still reports error 60, verify the PHP version selected in PHP Selector actually loads the curl and openssl extensions; both must be enabled under the Extensions tab. A cURL error 35 instead points at a TLS protocol mismatch, which happens when the receiver only accepts TLS 1.2 or 1.3 and an older PHP build negotiates something lower. Moving to a current PHP branch (8.2 or newer) in PHP Selector resolves that cleanly.

API Token Authentication and Request Logging

The REST API is the other half of this picture, and it fails for different reasons. BookStack API access uses per-user Token ID and Token Secret pairs generated on the user's profile page under Edit Profile → API Tokens. Requests must authenticate with the header Authorization: Token {id}:{secret}. The single most frequent failure on LiteSpeed is that the Authorization header never reaches PHP, producing a 401 Unauthorized even with correct credentials. LiteSpeed generally forwards it, but if a rewrite rule strips it, add this to the .htaccess in your document root:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP:Authorization} ^(.+)$
  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>

Other API rejections trace back to token state rather than transport. A token that has passed its optional expiry date returns a 401, and a user whose role lacks the Access system API permission is blocked regardless of a valid token; check that permission under Settings → Roles. For request-level troubleshooting, enable more verbose logging by setting APP_DEBUG and the log level in your BookStack .env file, editable through File Manager, then reproduce the call and read storage/logs/laravel.log. If you are building middleware that sits between BookStack and a genuine billing system, keep that transaction logging on the billing side; BookStack only records that the webhook or API event occurred, never any payment data. For diagnosing fatal errors on adjacent billing software, our write-up on FOSSBilling 500 errors and white screens covers a related debugging workflow.