All Activity
- Past hour
-
tylerscoffees2 joined the community
-
derek001 joined the community
- Today
-
adamandevefranchise changed their profile photo
-
adamandevefranchise joined the community
-
ambassadorlimos joined the community
-
irishmemorycards joined the community
-
rapidtags joined the community
-
slim started following Help!!! WHMCS Find My Shared Mailbox, Please! - M365 import
-
Help!!! WHMCS Find My Shared Mailbox, Please! - M365 import
slim replied to BENELUX's topic in Troubleshooting Issues
Yeah, Ive hit this - had to license the mailbox - so it has a direct login. 😞 - Yesterday
-
パナソニックの額 joined the community
-
Here is the hook by the way. Vote for its implementation if you think it's of any use. that hook sends the order details to the admin email and saves it in a txt file under "logs" folder
-
As you might know, whmcs has a problem with stripe: when the payment is not immediately confirmed by stripe (but requires to clic "capture" on stripe dashboard), whmcs does not record the order and the customer details and does not even send any notification to whmcs admin or to the customer. We solved with a hook that sends us an email with the order details, so that: - we know that there was a order attempt - we know that there might be a payment in stripe that requires manual "capture" - if necessary, we have all the client and order details in order to recreate the both client account and order. Here is the hook. <?php if (!defined("WHMCS")) { die("This file cannot be accessed directly"); } /** * Save all checkout data to a TXT file * AND email it to admin * Runs BEFORE order/invoice creation, and before redirect to Stripe. */ add_hook('ShoppingCartValidateCheckout', 1, function ($vars) { // --- 1. Clone and sanitise incoming data ------------------------------- $data = $vars; // Mask any possible card data (good practice even if Stripe never posts it) $sensitiveFields = [ 'ccnumber', 'cccvv', 'ccexpirymonth', 'ccexpiryyear', 'password', 'password2', ]; foreach ($sensitiveFields as $field) { if (!empty($data[$field])) { $data[$field] = '***masked***'; } } // --- 2. Attach cart contents from session (products, domains, etc.) --- $cart = isset($_SESSION['cart']) ? $_SESSION['cart'] : []; $payload = [ 'timestamp' => date('Y-m-d H:i:s'), 'ip' => $_SERVER['REMOTE_ADDR'] ?? null, 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null, 'checkoutFields' => $data, 'cart' => $cart, ]; // --- 3. Build a text representation (for file & email) ---------------- $entry = "==============================\n"; $entry .= "Checkout submitted: " . $payload['timestamp'] . "\n"; $entry .= "IP: " . ($payload['ip'] ?? 'N/A') . "\n"; $entry .= "User-Agent: " . ($payload['user_agent'] ?? 'N/A') . "\n\n"; $entry .= "DATA:\n" . print_r($payload, true) . "\n\n"; // --- 4. Save to TXT file ---------------------------------------------- $logDir = __DIR__ . '/../../logs'; $logFile = $logDir . '/pre_stripe_checkout.txt'; if (!is_dir($logDir)) { @mkdir($logDir, 0700, true); } @file_put_contents($logFile, $entry, FILE_APPEND); // --- 5. Send email to admin ------------------------------------------- // This uses WHMCS' built-in admin notification helper. // The "system" type means it goes to the system email / admin notifications. if (function_exists('sendAdminNotification')) { $subject = 'Pre-Stripe Checkout Captured'; // Use <pre> so the print_r formatting is readable in HTML email $message = nl2br(htmlspecialchars($entry)); sendAdminNotification('system', $subject, '<pre>' . $message . '</pre>'); } // IMPORTANT: Do NOT return an error. Just let checkout continue. return; }); I suggest to implement it. It would be great if it would offer some sort of customization in the admin area.
-
Aelfric Eden hoodie joined the community
-
BENELUX started following Help!!! WHMCS Find My Shared Mailbox, Please! - M365 import
-
Hey WHMCS Community So here’s the thing… I love WHMCS. It’s great. But right now, it has a little “quirk” when it comes to shared mailboxes. Imagine this: I’ve got my main mailbox doing its thing, happily bringing in emails. But then there’s the shared mailbox, the one that all the support team emails go to. WHMCS? It sees it… but then pretends it doesn’t exist. 😅 Here’s the geeky version: WHMCS currently uses /me/messages from Microsoft Graph API. This only fetches emails from the authenticated user’s mailbox. Shared mailbox? Nope, invisible. Even if Full Access is granted in Exchange and you’ve got all the delegated permissions like Mail.Read.Shared, WHMCS is like, “I don’t see anything here. Must be Monday.” The right way (according to Microsoft Graph) is: GET https://graph.microsoft.com/v1.0/users/{shared-mailbox}/mailFolders/inbox/messages Yes, WHMCS, that mailbox does exist, and yes, I do have permission to read it. What I’m asking: Can WHMCS please natively support shared mailbox imports? Guidance on exactly which delegated permissions are needed (so I don’t go on a wild goose chase for nothing). Support for the correct Graph API endpoint. Bonus points: documentation for optional PHP session settings for cross-site OAuth flows (because apparently, cookies are shy and need proper settings to stick around). Expected outcome: Fetch emails from shared mailboxes without needing a license for it. Less headache, more sanity. WHMCS behaving like the email superhero it’s meant to be. Anyone else banging their head on the “shared mailbox wall”? Let’s commiserate, share workarounds, and maybe get WHMCS to notice that shared mailboxes exist in the universe. Thanks!
-
birthturkey started following Email Verification for WHMCS
- Last week
-
coophosting started following Username generator on new order
-
Hey Bruxs In the first test today it looks working. He generated a username now, that looks good. Thank you for your time and helpfull answer!
-
ArkHost started following Email Verify Pro
-
Email Verify Pro Automates email verification with customizable blocking overlay. What it does: Sends verification email on registration Blocks unverified users with full-screen overlay Cryptographically secure 32-byte tokens Configurable expiry and rate limiting Full overlay customization (colors, fonts, animations) Admin verification status display Features: Token expiry configuration (default: 60 minutes) Resend cooldown (default: 2 minutes) Max resend attempts (default: 7 per 24 hours) Database-backed rate limiting CSRF protection on all endpoints Automatic token cleanup via daily cron Email change re-verification Compatible with WHMCS native verification Technical Details: Addon module with hook integration Uses existing WHMCS database columns (email_verified_at, email_verification_token, email_verification_token_expiry) Creates two tables: mod_email_verify_pro (settings), mod_email_verify_pro_rate_limit (tracking) No core table modifications Verification URL: index.php?m=email_verification&verify=TOKEN Languages: English, Russian, Dutch Requirements: WHMCS 8.0+, PHP 7.4+, MySQL 5.7+ Installation: Upload modules folder to WHMCS root Activate in Setup → Addon Modules Configure token expiry and rate limits Customize overlay appearance Edit email template in Setup → Email Templates → "Email Verification" Client Features: Receives verification email on registration Clicks link to verify Re-verify required on email change Resend verification email (rate limited) Admin Features: View verification status in client profile Configure token expiry Set rate limit parameters Full overlay styling control Automatic cleanup of expired tokens Why it exists: Built because WHMCS native email verification doesn't block access. Users could skip verification and access the client area. This module enforces verification with a blocking overlay and adds rate limiting to prevent abuse. Support: support@arkhost.com License: One per WHMCS install WHMCS Marketplace: https://marketplace.whmcs.com/product/8286-email-verify-pro Documentation: https://arkhost.com/knowledgebase/5545/Email-Verify-Pro-for-WHMCS.html Store: https://arkhost.com/store/whmcs-modules/email-verify-pro
-
We've finally solved with a hook that catch all the necessary data when the "pay now" button is clicked. I wonder why whmcs doesn't want to implement something like that... @whmcs team.. any reason???
-
Most of the users who sign up for whmcs are mostly to reduce the cost of creating a complete system, that includes the payments that whmcs supports, even if the website administrator has the ability to code, we still can't see or find the problem because the stripe module is encrypted by whmcs, most of the people here have tried to contact stripe, stripe pushes it to whmcs, in the end the people who actually pay to use the services of both sides have to find a solution to the problem of both sides.
-
I've not tried any alternatives - this issue aside, I do like WHMCS. Instead, I implemented Stripe directly into our website and just register the payments in WHMCS using the API. It was a bit painful because Stripe offers like a million different options, but it works now and we have 0 uncaptured payments.
-
Hello everyone, We’re pleased to announce the release of our new module: Spanish Fiscal E-Invoicing (VeriFactura / TicketBAI) for WHMCS from WHMCS Global Services. ✅ What it is This module automates e-invoicing in Spain and ensures full compliance with the antifraud regulations (VeriFactura) and the regional Basque requirement (TicketBAI). It integrates directly into your WHMCS billing system to handle Spanish-client invoicing. WHMCS Global Services+1 ✳ Key features Automatically generate invoices in WHMCS with the required XML format for Spain’s tax agency. WHMCS Global Services Real-time submission of invoice data to the Spanish tax agency (AEAT) via API. WHMCS Global Services Export structured XML data and CSV reports for your Spanish clients. WHMCS Global Services Full log and traceability of invoice generation and transmission. WHMCS Global Services Automatic activation only for clients whose country is Spain (so it won’t interfere with your global invoicing flows). WHMCS Global Services 🕒 Why act now From 1 January 2026, submission via VeriFactura becomes mandatory for companies / corporate tax payers. WHMCS Global Services From 1 July 2026, it becomes mandatory for all other businesses and self-employed workers. WHMCS Global Services If you operate in Spain (or have Spanish clients) and you’re using WHMCS, this module can save you from manual processes and possible compliance risk. 💡 License & pricing highlight Compatible with WHMCS 8.13.x and PHP 8.2 / 8.3. WHMCS Global Services We’re offering a 40% off introductory discount. WHMCS Global Services 10-Day money-back guarantee. WHMCS Global Services 🔧 Implementation & support The module is designed for straightforward setup. If you need help with configuration, API credentials (via partner Fiskaly), or guidance, the team provides support via ticketing and even remote assistance. WHMCS Global Services 👥 Who should consider it Hosting providers, SaaS businesses, service companies using WHMCS and servicing Spanish clients WHMCS resellers or multi-country operators with a Spanish billing component Any WHMCS user looking to automate Spanish tax-compliant invoicing easily If you’d like a demo or more details, feel free to contact our team or visit the product page. We’re happy to answer any questions or walk you through the setup. Thanks and looking forward to your feedback! Best regards, The WHMCS Global Services Team
-
If a client purchases a service using the credit on his account how come I cannot refund it to credit? I understand why you might not be able to refund to a credit card or PayPal since the funds did not come from that source. But I should always be able to refund to the credit balance. Now I have to add the credit manually and I cannot even mark the invoice refunded. I don't know what to do with the invoice now.
-
Webhook setup for New ticket generate in WHMCS
iBACT Digital replied to simranjeet's topic in General Discussion
I am Sharing below the Complete Code , that will run webhook and Send data to your webhook URL for both when a new ticket is created and When a ticket is replied, remember to update your webhook URL in the code, and use this code in public_html-whmcs-includes- hooks-(create a new file: ticketWebhook.php) and paste this code by updating ur webhook URL on the code <?php if (!defined("WHMCS")) { die("This file cannot be accessed directly"); } use WHMCS\Database\Capsule; /** * CONFIG */ const PRIMARY_WEBHOOK_URL = "YOUR WEBHOOK URL"; const ADMIN_REPLY_WEBHOOK_URL = "YOUR WEBHOOK URL"; /** * When false: admin replies will NOT be posted to PRIMARY_WEBHOOK_URL (only to ADMIN_REPLY_WEBHOOK_URL). * When true: admin replies are posted to BOTH URLs (legacy behavior). */ const SEND_ADMIN_REPLY_TO_PRIMARY = false; function wh_log($message) { try { logActivity("WebhookHook: " . $message); } catch (Exception $e) { // ignore } $file = '/tmp/whmcs_ticket_webhook.log'; $line = '[' . date('c') . '] ' . $message . PHP_EOL; @file_put_contents($file, $line, FILE_APPEND | LOCK_EX); } function get_ticket_reference($ticket) { $possibleFields = ['tid', 'ticketnum', 'ref', 'ticketid_public', 'ticketnumber', 'ticket_ref']; foreach ($possibleFields as $field) { if (isset($ticket->$field) && !empty($ticket->$field)) { return (string)$ticket->$field; } } if (!empty($ticket->title)) { $title = (string)$ticket->title; if (preg_match('/#?([A-Z]{2,6}-\d{3,12})/i', $title, $m)) { return '#' . strtoupper($m[1]); } if (preg_match('/#(\d{3,12})/', $title, $m2)) { return '#' . $m2[1]; } } return ''; } function fetch_ticket($ticketId) { return Capsule::table('tbltickets')->where('id', $ticketId)->first(); } function fetch_client($clientId) { return Capsule::table('tblclients')->where('id', $clientId)->first(); } function fetch_admin_name($adminId) { if (empty($adminId)) { return ''; } try { $admin = Capsule::table('tbladmins')->where('id', $adminId)->first(); if ($admin) { return trim(($admin->firstname ?? '') . ' ' . ($admin->lastname ?? '')) ?: ($admin->username ?? ''); } } catch (Exception $e) { // ignore } return ''; } function post_with_retries($url, $payload, $extraHeaders = [], $maxAttempts = 2) { $json = json_encode($payload); $attempt = 0; $lastError = null; while ($attempt < $maxAttempts) { $attempt++; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $json); $headers = array_merge(['Content-Type: application/json'], $extraHeaders); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 6); $resp = curl_exec($ch); $errno = curl_errno($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlErr = curl_error($ch); curl_close($ch); if ($errno === 0 && ($httpCode >= 200 && $httpCode < 300)) { wh_log("POST success to {$url} (attempt {$attempt}) http={$httpCode}"); return ['ok' => true, 'code' => $httpCode, 'body' => $resp]; } $lastError = "attempt {$attempt} failed: errno={$errno} http={$httpCode} curlErr={$curlErr}"; wh_log("POST error to {$url}: {$lastError}"); sleep($attempt); } return ['ok' => false, 'error' => $lastError]; } function build_base_payload($ticket, $client, $event, $isCreation = false) { $ticketId = $ticket->id ?? ''; $ticketRef = get_ticket_reference($ticket); if (empty($ticketRef)) { $ticketRef = 'TCKT-' . (string)$ticketId; } return [ "ticket_id" => (string)$ticketId, "ticket_ref" => (string)$ticketRef, "ticket_subject" => (string)($ticket->title ?? ''), "mobile" => (string)($client->phonenumber ?? ''), "client_name" => trim((string)($client->firstname ?? '') . ' ' . (string)($client->lastname ?? '')), "event" => $event, "timestamp" => date('c'), "ticket_status" => isset($ticket->status) ? $ticket->status : '', // explicit flag so receivers can easily know if it's a creation event "is_creation_event" => $isCreation ? true : false ]; } function send_primary_webhook($payload) { if (empty(PRIMARY_WEBHOOK_URL)) { wh_log("Primary webhook not configured; skip"); return; } $res = post_with_retries(PRIMARY_WEBHOOK_URL, $payload, ['X-Ticket-Event: ' . ($payload['event'] ?? '')]); if (!$res['ok']) { wh_log("Primary webhook POST failed: " . ($res['error'] ?? 'unknown')); } } function send_admin_reply_webhook($payload) { if (empty(ADMIN_REPLY_WEBHOOK_URL)) { wh_log("Admin reply webhook not configured; skip"); return; } $res = post_with_retries(ADMIN_REPLY_WEBHOOK_URL, $payload, ['X-Ticket-Event: ' . ($payload['event'] ?? '')]); if (!$res['ok']) { wh_log("Admin reply webhook POST failed: " . ($res['error'] ?? 'unknown')); } } function send_ticket_webhook($ticketId, $event, $isCreation = false) { try { $ticket = fetch_ticket($ticketId); if (!$ticket) { wh_log("Ticket not found (send_ticket_webhook): {$ticketId}"); return; } $client = fetch_client($ticket->userid ?? 0); $payload = build_base_payload($ticket, $client, $event, $isCreation); send_primary_webhook($payload); } catch (Exception $ex) { wh_log("Exception in send_ticket_webhook: " . $ex->getMessage()); } } function handle_admin_reply($vars) { try { $ticketId = $vars['ticketid'] ?? null; if (!$ticketId) { wh_log("handle_admin_reply: ticketid not found in vars"); return; } $ticket = fetch_ticket($ticketId); if (!$ticket) { wh_log("handle_admin_reply: ticket not found {$ticketId}"); return; } $client = fetch_client($ticket->userid ?? 0); // create payload and mark isCreation = false $payload = build_base_payload($ticket, $client, "ticket_replied_by_admin", false); // reply message $replyMessage = ''; if (!empty($vars['message'])) { $replyMessage = $vars['message']; } else { try { $lastReply = Capsule::table('tblticketreplies') ->where('tid', $ticketId) ->orWhere('ticketid', $ticketId) ->orderByDesc('id') ->first(); if ($lastReply && !empty($lastReply->message)) { $replyMessage = $lastReply->message; } } catch (Exception $e) { // ignore } } $payload['reply_message'] = (string)$replyMessage; // admin info $adminId = $vars['adminid'] ?? $vars['admin'] ?? null; $payload['admin_id'] = (string)$adminId; $payload['admin_name'] = fetch_admin_name($adminId); if (!empty($vars['ticketreplyid'])) { $payload['ticket_reply_id'] = (string)$vars['ticketreplyid']; } // Decide where to send: // - always send to ADMIN_REPLY_WEBHOOK_URL send_admin_reply_webhook($payload); // - optionally send to primary webhook if configured if (SEND_ADMIN_REPLY_TO_PRIMARY) { // mark event and send $payload['event'] = 'ticket_replied_by_admin'; $payload['is_creation_event'] = false; send_primary_webhook($payload); } else { wh_log("Admin reply not forwarded to primary (SEND_ADMIN_REPLY_TO_PRIMARY=false). Sent only to admin-reply webhook."); } } catch (Exception $ex) { wh_log("Exception in handle_admin_reply: " . $ex->getMessage()); } } /** * Hooks */ add_hook('TicketOpen', 1, function ($vars) { $ticketId = $vars['ticketid']; wh_log("Hook TicketOpen triggered for ticket {$ticketId}"); // creation event: mark isCreation = true send_ticket_webhook($ticketId, "ticket_created_by_client", true); }); add_hook('TicketOpenAdmin', 1, function ($vars) { $ticketId = $vars['ticketid']; wh_log("Hook TicketOpenAdmin triggered for ticket {$ticketId}"); // admin-created ticket: creation event send_ticket_webhook($ticketId, "ticket_created_by_admin", true); }); add_hook('TicketUserReply', 1, function ($vars) { $ticketId = $vars['ticketid']; wh_log("Hook TicketUserReply triggered for ticket {$ticketId}"); send_ticket_webhook($ticketId, "ticket_replied_by_client", false); }); add_hook('TicketAdminReply', 1, function ($vars) { $ticketId = $vars['ticketid'] ?? null; wh_log("Hook TicketAdminReply triggered for ticket " . ($ticketId ?? 'unknown')); handle_admin_reply($vars); }); -
Arash Tavana changed their profile photo
-
WhatsApp Notification addon for WHMCS API-based
JNDEVELOPER replied to JNDEVELOPER's topic in Third Party Add-ons
Update v 1.0.4 based on feedback from peers using this addon. Added a Monitoring system and Queue system to prevent overloading the WHMCS database during data iteration. UI and Ajax system optimization within the queue system. Added Event Type / Hook Type to the queue system. Added several event notifications. Improved error handling for compatibility with the latest WHMCS version. Optimized integration with several WhatsApp gateway providers, resolving frequent timeout issues previously experienced with one specific provider. Tested: Successfully sent 3600 messages in 1 hour to 200+ WhatsApp numbers. -
rapidtags started following Error: the single id was not found in response
-
Hi, to fix the issue go to Settings > General Settings, under Ordering Tab look for "Enable Random Usernames" uncheck it if checked.
-
Hello, I am looking for a WHMCS developer who can create a custom Telegram integration for my instance. The goal is to receive real-time Telegram notifications for key events such as: New support tickets opened New support ticket replies New invoices generated Newly paid invoices New client registrations Requirements: Must be implemented using WHMCS hooks Should send messages to a specified Telegram group via a telegram bot Ability to guide through setup and deployment is appreciated If you are experienced with WHMCS hooks and Telegram Bot API development, please contact me. Email: vpsworker@proton.me Thank you.
-
*they will not look...
