xiconsulting2
Retired Forum Member-
Posts
9 -
Joined
-
Last visited
Content Type
Profiles
Forums
Events
Hotfixes
Everything posted by xiconsulting2
-
In the new release there apparently are some new API functions...does anyone have any idea when these are going to be documented? For example Add Contact is supposed to be in the new API, and I see it in the API folder, but there is no documentation for this function. Thanks!
-
The actual processing takes place by calling individual functions and passing them along to other functions depending on the outcome, there definitely are optimizations to be made - but basically we used our own validation via PHP/xajax, and if all validates, pass it along to various API functions. 1. Validate form, return the errors if there are any 2. if ok, process payment (again with our service, they HAVE to have paid before we establish their account, we do not want to be using an invoicing system for the first order because it confuses the elderly, we had an 80 year old woman sign up for our service with the old WHMCS template based form, and she did not know what to do to pay an invoice - we have since provided better instructions for users) (pass transaction ID to next function) 3. Then use API Create Client (our clients service does not allow any more than one product per user, so at least for our needs there is no login form to "pre-fill" the account information, although it could definitely be done) (pass client id to next function) 4. Then API add order (pass order id/invoice id/transaction id/client id to next function) 5. Then API mark invoice, pass client id and order ID to next function 6. Then API accept order, pass client id to next function 7. Finally, do create module function and in XAJAX we do: $redirecturl = "https://whmcs.domain.com/members/dologin.php?username=$email&password=$password&goto=authentication"; $objResponse->redirect($redirecturl, 0); which does a redirect to log the user in and then provides the page where their order is confirmed and they may continue to activate their account. Of course between each of these steps, we needed to account for an error in processing, or if there is some snag between the database or WHMCS, chaining these functions needs to be logged, so we do keep a log of what is going on during the order - and return a jquery growl alert telling the user to not submit their order again and to contact customer support. We also disable the submit button during and after processing until errors are corrected. Sorry its a little confusing, it was a unique situation but we plan to clean it up a little more or if possible do everything directly with the DB and pass the API altogether.
-
HOWTO: Retreiving Promotion Code Validity
xiconsulting2 replied to xiconsulting2's topic in Developer Corner
Oh, just fyi, I fixed it in the post - but not in the attached version, if you use the attached text file be sure to replace the following: $rsd = mysql_query("select * from tblpromotions where code='$pc'"); $msg = mysql_num_rows($rsd); // this returns 0 if not exist with: $rsd = mysql_query("select * from tblpromotions where code='$pc'"); $pcexist = mysql_num_rows($rsd); // this returns 0 if not exist -
I tried to enter this in tips and tricks, but I guess it would have to be moved if deemed helpful. A while back I had looked for something to do an external check on the promo code database in order to integrate it with an external (API) based form, and did not really have any luck. So, this code I am posting does work - but do keep in mind the comments, code structure, and everything does work, but is not optimized fully - the actual version we converted to an API function and integrated with XAJAX and JQuery on the front end. Anyway, I figured that if I needed to find something like this and it was not available, that I would provide my previous script since I am sure someone else has looked for it before. I would release the XAJAX API version, but I am unable to publicly release it (not because it was very difficult, but because my client agreement does not allow me to release any code that is actually in use). There are more efficient ways of doing the checks which we implemented in the API version, but again I just cannot release that code at this time, and I am sure the WHMCS developers have already created this possibility for future API versions - but for now, it does work. Anyway, here you go - again, unrefined but it is a way to check promo code validity (expiration, product type, and billing cycle only) and return its values in an array. I do not have much time to assist in anything, but if you have a small question I would probably be happy to HELP (not give beginner PHP lessons). I am posting this to help others LEARN or at least take some initiative to learn - I did take about an hour or so to comment the code before this post, so it should be pretty self explanatory. I have also attached a text file, if you want to test this on your server (has to reside on your whmcs server), just rename to *.php and be sure to edit the form drop down product id's, and also the db values and it should be good to go. <?php /** * @author Amir W Husain * @license You are free to use this code, but we are not responsible if it destroys your business, your life, your car, or anything else. This script does not write against your database, it only reads it - so it is unlikely to do any damage. But as always, backup your database before doing any additional development. */ ## START OF THE CHECK PROMO CODE FUNCTION BELOW ## ## FUNCTION INPUT VARS EXPLANATION ## ## $promocode = the promocode entered on your order form ## $selectedplan = this would be the id of the plan that the user has chosen ## $selectedcycle = this is the billing cycle selected - passed as Monthly, Annually, etc. Our services only offer monthly ## and annual billing so if you offer other forms of billing you are on your own. ## Note: You could add additional checks here, these just happen to be the only ones that currently matter for what we do with promocodes. ## RESPONSE EXPLANATION ## ## The below function always returns an array called $pr and will always ## return in the array $pr['valid'] which will equal either 0 for false ## and 1 for true - and $pr['reason'] which will explain the reason why ## the code was not accepted. On acceptance this value is still sent back, ## its just blank. function checkPromo($promocode,$selectedplan,$selectedcycle) { /** WHMCS database connection information. * This is not really where the DB login information comes from in our live script, but its good for a quick and easy test. */ $dbhost = "localhost"; //usually unchanged $dbname = "database_1"; //enter your whmcs database name here $dbuser = "root"; //enter your database username here $dbpass = "rootpass"; //enter your password here /** * ESTABLISH DATABASE CONNECTION - This would usually be done outside of the function but again we are doing it this way for simplicity) */ $con = mysql_connect("$dbhost", "$dbuser", "$dbpass"); if ($con) { mysql_select_db("$dbname", $con); } else { die("Could not connect to database"); } /** * CREATE SHORT VARIABLES - The below is totally not neccessary, just shortening the values from the function to be shorter */ $pc = $promocode; $plannum = $selectedplan; // 4, 6, 8 in our case $bc = $selectedcycle; // Annually or Monthly /** * CHECK DATABASE - for the existence of entered promo code */ $rsd = mysql_query("select * from tblpromotions where code='$pc'"); $pcexist = mysql_num_rows($rsd); // this returns 0 if not exist if ($pcexist=="0") // the promo code does not exist in the table { // build response array with minimum values $pr['promocode'] = $pc; $pr['valid'] = 0; $pr['reason'] = 'Promo code not found.'; // oh well, at least it was quick! return the array return $pr; } elseif ($pcexist!="0") // the code exists, so get the rest of the fields from the database relating to that code { // FYI, we are only retreiving the fields we are actually using so if you have other fields in the whmcs promotions table that you use, // you'll need to code those functions. If you do decide to do this, its a good idea to think logically, the order at which the validation // is done is very important (for example it is not worth checking the rest of the values if the code is expired...so we check that first, // and so on to limit the response time to the user and limit unneccessary server processing ) $query = "SELECT type, value, cycles, appliesto, expirationdate, maxuses, uses FROM tblpromotions WHERE code='$pc'"; $result = mysql_query($query); // build variables from the returned db array while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { // date functions (these arent returned from the db, but from the server to determine validity) $thedate = date("Y-m-d"); $todaysdate = strtotime($thedate); $type = $row['type']; // We only provide percentage discounts, but you could used fixed values in your script by adding additional logic $value = $row['value']; // The value of the code $cycles = $row['cycles']; // The billing cycles that the code applies to $appliesto = $row['appliesto']; // The product types the code applies to $expirationdate = $row['expirationdate']; // The expiration date of the code, we assume that if the exp date is 0000-00-00 or empty, that there is no exp $expires = strtotime($expirationdate); // the expiration date formatted so we can check against todays date $maxuses = $row['maxuses']; // Maximum uses of the code $uses = $row['uses']; // How many times the code has been used } /** * NOW WE DO THE VALIDATION! */ ## 1. check if expired // ok promocode is not past expiry, so we continue // Add the promo code given to the array response so we know what was entered and can provide it back to the user - this value is given for all responses regardless of outcome. $pr['promocode'] = $pc; if ($expires >= $todaysdate OR $expirationdate == "0000-00-00") { ## 2. now check if valid for the chosen products and term // generate arrays for the billing cycles and for the valid products $cyclesarray = explode(',', $cycles); // outputs the valid cycles which are separated by commas into an array for processing $productsarray = explode(',', $appliesto); // outputs the products which are separated by commas into an array for processing // just add these to the response right now since they will not change from this point on and no point in adding them in the if statements $pr['validproducts'] = $productsarray; // products that this code is valid for $pr['validcycles'] = $cyclesarray; // provide the cycles that this code is valid for so that the user could try changing their plan to one it would work on if (in_array($bc, $cyclesarray) OR $cycles == "") { // ok, promocode is valid for the billing cycle selected ## 3. continue to check if valid for specified product if (in_array($plannum, $productsarray)) { // ok, code is valid for billing cycle and product selected ## 4. Check if code has passed its maximum number of uses // do math to subtract used number from maximum number to see if this code can be used again $usesleft = $maxuses - $uses; if ($usesleft > 0) { ## GREAT! Product code has some free uses left, is not expired, is valid for the selected billing cycle, and is valid for the selected product ## congrats, our code is valid and we can notify the user and apply their discount // this is the only VALID response we give to users, since a passcode can only be accepted or rejected and there is no inbetween $pr['valid'] = 1; // promo is valid $pr['errorcode'] = 0; // there is no error code $pr['discounttype'] = $type; // percent or fixed? $pr['discountvalue'] = $value; // the value of the promo code (use the discounttype response to create logic for % or $ calculation) $pr['expirationdate'] = $expirationdate; $pr['maxuses'] = $maxuses; // maximum number of uses $pr['usesleft'] = $usesleft; // number of uses left before code is no longer valid $pr['uses'] = $uses; // number of times code was used return $pr; } else ## CHECK 4 FAILED because the maximum code uses has passed { $pr['valid'] = 0; $pr['errorcode'] = 4; $pr['reason'] = 'Maximum promo code use has been reached'; $pr['expirationdate'] = $expirationdate; $pr['maxuses'] = $maxuses; $pr['usesleft'] = $usesleft; $pr['uses'] = $uses; return $pr; } } else ## CHECK 3 FAILED because the code is not valid for the selected product that the user has chosen { $pr['valid'] = 0; $pr['errorcode'] = 3; $pr['reason'] = 'Not valid for selected product type'; $pr['expirationdate'] = $expirationdate; return $pr; } } else ## CHECK 2 FAILED because the code is not valid for the billing cycle the user has chosen { $pr['valid'] = 0; $pr['errorcode'] = 2; $pr['reason'] = 'Not valid for selected billing cycle'; $pr['expirationdate'] = $expirationdate; return $pr; } } elseif ($expires <= $todaysdate) ## CHECK 1 FAILED because promo code has expired, or your servers date and time is screwed up { $pr['valid'] = 0; $pr['errorcode'] = 1; $pr['reason'] = 'Expired'; $pr['expirationdate'] = $expirationdate; return $pr; } else ## CHECK 1 FAILED because something went wrong when checking if code was expired, could be that the date is not being calculated correctly { $pr['valid'] = 0; $pr['errorcode'] = 98; $pr['reason'] = 'Unknown Error While Determining Expiration'; $pr['expirationdate'] = $expirationdate; return $pr; } } else ## CHECK 0 FAILED because something went wrong when checking for the promo code, but we don't know what so we just respond with invalid { $pr['valid'] = 0; $pr['errorcode'] = 99; $pr['reason'] = 'Unknown error when checking for promo code validity, please try again.'; return $pr; } } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>WHMCS Promo Code Test</title> <style> html,body{font-family:Arial, Helvetica, sans-serif;} label{display:block;padding:5px;width:200px;font-size:10px;margin-bottom:10px;} input{width:100%;padding:5px;} select{width:100%;padding:5px;} button{padding:10px;font-family:Arial, Helvetica, sans-serif;} .error{padding:10px;margin:5px;font-size:16px;background-color:#C00;color:#fff;} .success{padding:10px;margin:5px;font-size:16px;background-color:#9C0;color:#fff;} </style> </head> <body> <form id="pctestform" name="pctestform" method="post" action="<?php echo $PHP_SELF; ?>"> <label>Promo Code <input type="text" name="promocode" id="promocode" tabindex="1" /> </label> <label>Product Type <select id="plan" name="plan"> <!-- Be sure to change these to your product codes, optionally you could do make a php script to just populate dynamically --> <option value="4">Advanced Plan</option> <option value="6">Premium Plan</option> <option value="8">Superior Plan</option> </select> </label> <label>Billing Cycle <select id="cycle" name="cycle"> <!-- Only Annually and Monthly billing cycles are checked, if you have others that is OK, as long as the code is also valid for the following as well --> <option value="Annually">Billed Annually</option> <option value="Monthly">Billed Monthly</option> </select> </label> <button name="submit" type="submit">Validate Promo Code</button> </form> <hr /> <?php ## HOW TO USE SAMPLE ## # Don't actually use this in your script, its just for testing to show you how to call the # function we will use just use POST variables, these are not going to be sanitized or # anything so again DO NOT USE POST in production without some kind of validation. if($_POST){ if($_POST["promocode"]!=""){ $enteredpromo = $_POST["promocode"]; $plannumber = $_POST["plan"]; $plancycle = $_POST["cycle"]; // Call the function and get the response. $response = checkPromo($enteredpromo,$plannumber,$plancycle); // Print the resulting array print "<h1>Response Output Array</h1><pre>"; var_dump($response); print "</pre>"; // Print an individual value for the array, for example if the code is valid or not, show something print "<h1>User Friendly Response Example:</h1>"; if($response["valid"]!=1){ // show error if code is not valid echo '<p class="error"><strong>Sorry!</strong> Promocode' . $response["promocode"] . ' is not a valid promo code for the following reason: ' . $response["reason"] . '</p>'; } else { // show if code is valid - these variables don't have to be printed, but could be used in your script or could update your order form using javascript (which is what we did with XAJAX) echo '<p class="success"><strong>Congrats!</strong> Promocode ' . $response["promocode"] . ' was accepted! You are now saving ' . $response["discounttype"] . $response["value"] . ' on your order!</p>'; } }else{ echo '<p class="error"><strong>Sorry!</strong> You have to at least enter a promo code to check!</p>'; } } ####### END EXAMPLE ######## ?> </body> </html> whmcs-promo-code-check.txt
-
I agree, if you check out my latest posts regarding many extensive modifications, I have not been happy with any other script. And I have spent thousands of dollars on various "membership" applications but none have offered the amount of flexibility that WHMCS has even with closed source. We are utilizing WHMCS for a non-hosting related site, and were able to customize it to create a fully automated order and fulfillment process. Good stuff. We have also been able to stay PCI compliant with WHMCS which I was surprised about.
-
I would have to say that after creating such an advanced script and completely coding many custom behaviors, functions, and what not - WHMCS has been extremely flexible even with closed code. We were able to do everything we had set out to do, although it takes a little more time, its very simple to easily create new API functions. We additionally have created an entirely new support frontend Adobe AIR application with Flex - which was based on some of the existing API functions along with some new ones we created (we converted all of the API calls into REST format to make sure the structure of all of our API calls are the same regardless of the underlying script). Many of the custom API functions work directly through the database, or make multiple calls using the WHMCS API. We also utilize the subaccounts feature, but in a different way (for spouse accounts). At first it seemed a bit hopeless with closed source, but with some creativity, patience, and a decent amount of php - pretty much any obstacle can be overcome. Anyone could create a form similar to this, would have been nice to get beta access though as it woulda been nice to test and provide some feedback.
-
We did this with WHMCS here, was waiting for beta access to test AJAX form - but never received it so coded our own using XAJAX, the WHMCS API and hooks for fulfillment. Still some code cleanup (especially with the javascript) but it does work well for us. We have some stuff in there to check for date of birth (older than 18), existing members, etc in addition to working with the WHMCS promo code database to determine the validity of the codes. http://bit.ly/a1yB9T
-
The easier route would be to customize the template to match your design, we DID do something like this using XMLHttpRequest, and not including the header/footer if it detects it was an AJAX request (so the header and footer detect if a query string is there that determines its ajax request) - if the query string = 0 it will just load header1.tpl and footer1.tpl, if it = 1 then it will set the header and footer to blank files and load in the rest of the page. It was a bit cumbersome and was not "best practice" since some users would have issues without javascript - I am sure there is a better way to do it, but this is how it was accomplished first time around - but WHMCS templates are pretty customizable and the best way to do it now would probably be to just take the time to match the css and convert all the TPL files to match
