Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 11/09/25 in Posts

  1. your API call isn’t using the filter array you think it is, and you’re reading the wrong field from the response. 1103 = totalresults for all unpaid invoices (because filters weren’t applied). 25 = numreturned default page size, with no limitnum specified. $clientid = (int) $vars['params']['userid']; $command = 'GetInvoices'; $postData = array( 'userid' => $clientid, 'status' => 'Unpaid', 'limitstart' => 0, 'limitnum' => 3000, ); $getClientInvoices = localAPI($command, $postData); // , $adminuser if needed $invoicecount = 0; if ($getClientInvoices['result'] === 'success') { $invoicecount = (int) $getClientInvoices['totalresults']; } if ($invoicecount >= 1) { logActivity( 'Service renewal will be aborted for client ' . $clientid . '. It is not qualified because it has ' . $invoicecount . ' unpaid invoices - by hook_send_renewal_notice_toadmin...' ); }
    1 point
  2. Hello, It's very easy to resolve with Hook, just do Create a hook file at: whmcs/includes/hooks/invoice-lineitem-date.php <?php use Illuminate\Database\Capsule\Manager as Capsule; use Carbon\Carbon; add_hook('InvoiceCreationPreEmail', 1, function ($vars) { $invoiceId = (int) $vars['invoiceid']; $inv = Capsule::table('tblinvoices')->where('id', $invoiceId)->first(); if (!$inv) { return; } $dateText = Carbon::parse($inv->date)->format('d-m-Y'); $items = Capsule::table('tblinvoiceitems') ->where('invoiceid', $invoiceId) ->get(); foreach ($items as $item) { $isBillableItem = ($item->type === 'Item'); if (!$isBillableItem) { continue; } $newDescription = $item->description; if (strpos($newDescription, '{DAY}') !== false || strpos($newDescription, '{MONTH}') !== false || strpos($newDescription, '{YEAR}') !== false) { $newDescription = preg_replace( '/\{DAY\}\s*-\s*\{MONTH\}\s*-\s*\{YEAR\}/i', $dateText, $newDescription ); } else { $newDescription .= " ({$dateText})"; } Capsule::table('tblinvoiceitems') ->where('id', $item->id) ->update(['description' => $newDescription]); } });
    1 point
  3. Hello, It’s clear that there is an issue with your database configuration. First, try connecting to the MySQL database using the credentials provided in your configuration.php file and check whether the connection is successful.
    1 point
  4. I'd suggest that your average user that grabs a script like this will not change anything in it, especially since they're not (apparently) being told to. I've seen users not change a "CHANGE_THIS" in scripts, then ask why it's not working. I've also seen them ignore the "remove this file after use", so relying on that might not be advisable either. Best practice is not to spoon feed, keep this outside the publicly accessible directories, and instead of that simple URL parameter you reveal, make it a "secret key" that is defined within the script, with a note to change it (or better still they make their own that must be x long and have this or that in it; if empty, fail with "key needed"). Safety first.
    1 point
  5. 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.
    1 point
  6. Hi, to fix the issue go to Settings > General Settings, under Ordering Tab look for "Enable Random Usernames" uncheck it if checked.
    1 point
  7. In the v9.0 release cycle we have focused efforts on updating our dependencies - which was quite a significant undertaking - ensuring that we carefully managed the Smarty update (as that is potentially an area of high user-impact). I anticipate this work will enable the testing and validation of future PHP versions to move much faster, and we expect to deliver PHP 8.4 support in v9.1. Please see the finalised list above. TCPDF will be updated to 6.10.
    1 point
  8. Yes, this behavior is expected. The /vendor/ directory in WHMCS contains core dependency files and is not intended to be accessed directly via the browser. For security reasons, WHMCS restricts access to this directory, and attempting to access it should result in a 404 error rather than a 403. This is by design to prevent exposure of sensitive files. The .htaccess file inside the /vendor/ directory is part of this protection, but depending on your server configuration, the parent .htaccess in public_html and Apache’s rewrite rules may also influence how errors are handled. Removing or modifying the parent .htaccess can interfere with WHMCS’s routing and URL handling, which is why you noticed a change in behavior when doing so. In general, you should not need to access anything inside /vendor/ via the browser, and it’s best to leave the default .htaccess WHMCS rules in place to ensure WHMCS functions securely and correctly. These are the stock WHMCS Rules: https://docs.whmcs.com/8-13/system/general-settings/general-settings-general/#whmcs-rules I hope this information helps.
    1 point
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated