Chargebee webhooks
Chargebee tells the paid app about billing changes through four webhooks. They are set up on the live site under Settings > Configure Chargebee > API Keys and Events > Webhooks:
- NG Payment Succeeded calls
/paymentSucceeded. This is the only event that moves an order'suntil_dateforward, so a missed call leaves a paying customer locked out once their old date passes. See Unpaid invoices. - NG Cancel Subscription calls
/cancelSubscription. - NG Change Subscription calls
/changeSubscription. - NG Renew Subscription calls
/renewSubscription. The app ignores the renewal event on purpose; the payment that follows it does the work.
Each URL ends in a secret-key query parameter that the app checks. Keep the key when editing a URL, and do not copy it into tickets or documents.
Use the backend host, not app.nutritionistpro.com
The webhooks call the paid backend directly:
https://api.nexgen-v3.prod.nutritionistpro.net/<path>?secret-key=...
That host resolves straight to the prod AWS load balancer, which presents the Amazon RSA certificate for *.prod.nutritionistpro.net (see Certificates).
Do not point them at https://app.nutritionistpro.com/.... The frontend's nginx does proxy the four paths to the backend, so the URL looks like it works when tested with curl, but app.nutritionistpro.com is behind Cloudflare. Since Cloudflare reissued the zone's edge certificate on October 23, 2026, the nutritionistpro.com zone presents an ECDSA certificate only and refuses every RSA handshake. Chargebee's webhook client could not connect, and every call failed with an SSL error until the URLs were moved to the backend host on October 28, 2026. The axxya.com zone in the same Cloudflare account still offers RSA, so the difference is easy to miss. See Cloudflare.
The "SSL failure" email
When a call fails, Chargebee emails the account owner with the subject line about a webhook failing to respond with HTTP 200, the webhook name and id, and this reason:
The SSL communication has failed. Please ensure that you have right SSL certificate.
That reason means the TLS handshake with the host failed. The app never received the request, so nothing appears in the Laravel log. Check the host the webhook calls, not the app.
-
Note which webhook failed and its URL host in the Chargebee webhooks list.
-
Check that the host accepts an RSA handshake. It should print
Cipher is ECDHE-RSA-AES128-GCM-SHA256, notCipher is (NONE):h=api.nexgen-v3.prod.nutritionistpro.netecho | openssl s_client -connect $h:443 -servername $h -tls1_2 -cipher ECDHE-RSA-AES128-GCM-SHA256 2>&1 | grep -oE "Cipher is [^ ]+"The backend host has an RSA certificate only, so the same test with an ECDSA cipher prints
(NONE)there. That is expected and is not the problem. -
Check the certificate's names and expiry date:
echo | openssl s_client -connect $h:443 -servername $h 2>/dev/null | openssl x509 -noout -subject -issuer -dates -
Open the failed event from the link in the email (Logs > Events, filtered to the webhook) to see the error Chargebee recorded for each attempt.
A failed RSA handshake on a nutritionistpro.com host means the webhook is pointing at Cloudflare again. An expired or mismatched certificate on the .net host is an ACM problem; see Certificates.
After a fix
Use Test Webhook on each of the four webhooks and check that it reports a 200.
Chargebee keeps retrying a failed event for a while on its own, so events from a short outage usually arrive without any action. An event that ran out of retries can be resent from its page under Logs > Events. Resend every missed Payment Succeeded event: until it arrives, the customer's until_date stays where it was, and they lose access when that date passes.
Related
- Unpaid invoices - what each handled event changes in the app, and the "no local order matched" alert.
- Refunds - cancellation and refund steps in Chargebee.
- Cloudflare - the
nutritionistpro.comzone and its edge certificate. - Certificates - the ACM certificates on the load balancers.