Search code examples
phpxmlweb-servicesquickbooks-onlineqbxml

How to set .QBW file path for response in Authenticate for Quickbooks Web Connector?


Ok, I am using Keith Palmers excellent classes for this, located here: https://github.com/consolibyte/quickbooks-php, and the Web Connector is working fine, but only when I have Quickbooks open and click on File->Update Web Service within the Server the Web Connector is working. Once I log out of the Server, or even close the Quickbooks application, the Web Connector no longer works. I am running Quickbooks Enterprise, and have read that I need to send a response from the authenticate() method, which I'm guessing that the Web Connector server (https) needs to send, which is the <AppURL> from within the .QWC file. The only problem is, I don't fully understand how Keith's code is working and not even sure where the authenticate() method is being ran at. And it could be possible that this option is already available in a Class and/or Function/Hook that seems to be so many of within the QB Classes.

Right now, my server has very little markup and looks like this:

<?php

/**
 * Example QuickBooks SOAP Server / Web Service
 * 
 * This is an example Web Service which adds customers to QuickBooks desktop 
 * editions via the QuickBooks Web Connector. 
 * 
 * MAKE SURE YOU READ OUR QUICK-START GUIDE:
 *  http://wiki.consolibyte.com/wiki/doku.php/quickbooks_integration_php_consolibyte_webconnector_quickstart
 *  http://wiki.consolibyte.com/wiki/doku.php/quickbooks
 * 
 * You should copy this file and use this file as a reference for when you are 
 * creating your own Web Service to add, modify, query, or delete data from 
 * desktop versions of QuickBooks software. 
 * 
 * The basic idea behind this method of integration with QuickBooks desktop 
 * editions is to host this web service on your server and have the QuickBooks 
 * Web Connector connect to it and pass messages to QuickBooks. So, every time 
 * that an action occurs on your website which you wish to communicate to 
 * QuickBooks, you'll queue up a request (shown below, using the 
 * QuickBooks_Queue class). 
 * 
 * You'll write request handlers which generate qbXML requests for each type of 
 * action you queue up. Those qbXML requests will be passed by the Web 
 * Connector to QuickBooks, which will then process the requests and send back 
 * the responses. Your response handler will then process the response (you'll 
 * probably want to at least store the returned ListID or TxnID of anything you 
 * create within QuickBooks) and this pattern will continue until there are no 
 * more requests in the queue for QuickBooks to process. 
 * 
 * @author Keith Palmer <[email protected]>
 * 
 * @package QuickBooks
 * @subpackage Documentation
 */

// I always program in E_STRICT error mode... 
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);

// We need to make sure the correct timezone is set, or some PHP installations will complain
if (function_exists('date_default_timezone_set'))
{
    // * MAKE SURE YOU SET THIS TO THE CORRECT TIMEZONE! *
    // List of valid timezones is here: http://us3.php.net/manual/en/timezones.php
    date_default_timezone_set('America/New_York');
}

// Require the framework
require_once(dirname(__FILE__) . '/../QuickBooks.php');

$user = 'quickbooks';
$pass = 'password';

// Map QuickBooks actions to handler functions
$map = array(
    QUICKBOOKS_ADD_CUSTOMER => array( '_quickbooks_customer_add_request', '_quickbooks_customer_add_response' ),
    QUICKBOOKS_QUERY_CUSTOMER => array('_quickbooks_query_customer_request', '_quickbooks_query_customer_response'),
    // ... more action handlers here ...
    );

// This is entirely optional, use it to trigger actions when an error is returned by QuickBooks
$errmap = array(
    // 3070 => '_quickbooks_error_stringtoolong',               // Whenever a string is too long to fit in a field, call this function: _quickbooks_error_stringtolong()
    // 'CustomerAdd' => '_quickbooks_error_customeradd',    // Whenever an error occurs while trying to perform an 'AddCustomer' action, call this function: _quickbooks_error_customeradd()
    'CustomerQuery' => '_quickbooks_error_customer_query',
    '*' => '_quickbooks_error_catchall',                // Using a key value of '*' will catch any errors which were not caught by another error handler
    // ... more error handlers here ...
    );

// An array of callback hooks
$hooks = array(
    // There are many hooks defined which allow you to run your own functions/methods when certain events happen within the framework
    // QuickBooks_WebConnector_Handlers::HOOK_LOGINSUCCESS => '_quickbooks_hook_loginsuccess',  // Run this function whenever a successful login occurs
    );

function _quickbooks_error_customer_query($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg)
{
    file_put_contents(dirname(__FILE__) . '/customer_query_error.txt', "Error Number " . $errnum . PHP_EOL . $errmsg . PHP_EOL, FILE_APPEND | LOCK_EX);
}

function _quickbooks_error_catchall($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg)
{
    mysql_query("
        UPDATE 
            my_customer_table 
        SET 
            quickbooks_errnum = '" . mysql_real_escape_string($errnum) . "', 
            quickbooks_errmsg = '" . mysql_real_escape_string($errmsg) . "'
        WHERE 
            id = " . (int) $ID);
}

// Logging level
$log_level = QUICKBOOKS_LOG_DEBUG;              
$soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN;

$soap_options = array(      // See http://www.php.net/soap
    );

$handler_options = array(
    //'authenticate' => ' *** YOU DO NOT NEED TO PROVIDE THIS CONFIGURATION VARIABLE TO USE THE DEFAULT AUTHENTICATION METHOD FOR THE DRIVER YOU'RE USING (I.E.: MYSQL) *** '
    //'authenticate' => 'your_function_name_here', 
    //'authenticate' => array( 'YourClassName', 'YourStaticMethod' ),
    'deny_concurrent_logins' => false, 
    'deny_reallyfast_logins' => false, 
    );      // See the comments in the QuickBooks/Server/Handlers.php file

$driver_options = array(        // See the comments in the QuickBooks/Driver/<YOUR DRIVER HERE>.php file ( i.e. 'Mysql.php', etc. )
    //'max_log_history' => 1024,    // Limit the number of quickbooks_log entries to 1024
    //'max_queue_history' => 64,    // Limit the number of *successfully processed* quickbooks_queue entries to 64
    );

$callback_options = array(
    );

$dsn = 'mysql://{my_db_name}:{my_db_password}@localhost/{my_db}';

// Create a new server and tell it to handle the requests
// __construct($dsn_or_conn, $map, $errmap = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_PHP, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array(), $callback_options = array()
$Server = new QuickBooks_WebConnector_Server($dsn, $map, $errmap, $hooks, $log_level, $soapserver, QUICKBOOKS_WSDL, $soap_options, $handler_options, $driver_options, $callback_options);
$response = $Server->handle(true, true);


function _quickbooks_customer_add_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
    // Grab the data from our MySQL database
    $arr = mysql_fetch_assoc(mysql_query("SELECT * FROM my_customer_table WHERE id = " . (int) $ID));

    $xml = '<?xml version="1.0" encoding="utf-8"?>
        <?qbxml version="2.0"?>
        <QBXML>
            <QBXMLMsgsRq onError="stopOnError">
                <CustomerAddRq requestID="' . $requestID . '">
                    <CustomerAdd>
                        <Name>' . $arr['name'] . '</Name>
                        <CompanyName>' . $arr['name'] . '</CompanyName>
                        <FirstName>' . $arr['fname'] . '</FirstName>
                        <LastName>' . $arr['lname'] . '</LastName>
                    </CustomerAdd>
                </CustomerAddRq>
            </QBXMLMsgsRq>
        </QBXML>';

    return $xml;
}


function _quickbooks_customer_add_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{   
    mysql_query("UPDATE my_customer_table SET quickbooks_listid = '" . mysql_escape_string($idents['ListID']) . "' WHERE id = " . (int) $ID);

}

// Get all Customers!
function _quickbooks_query_customer_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale) {
    $xml = '<?xml version="1.0" encoding="utf-8"?>
        <?qbxml version="11.0"?>
        <QBXML>
            <QBXMLMsgsRq onError="stopOnError">
                <CustomerQueryRq requestID="' . $requestID . '">
                    <MaxReturned>10000</MaxReturned>
                    <ActiveStatus>All</ActiveStatus>
                </CustomerQueryRq>
            </QBXMLMsgsRq>
        </QBXML>';

    return $xml;
}

function add_quotes($value)
{
    if (is_string($value))
        return sprintf("'%s'", mysql_escape_string($value));
    else
        return $value;
}

function _quickbooks_query_customer_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
    $customers = json_decode(json_encode(simplexml_load_string($xml, "SimpleXMLElement", LIBXML_NOCDATA)), true);

    if (!empty($customers['QBXMLMsgsRs']) && !empty($customers['QBXMLMsgsRs']['CustomerQueryRs']) && !empty($customers['QBXMLMsgsRs']['CustomerQueryRs']['CustomerRet']))
    {
        foreach($customers['QBXMLMsgsRs']['CustomerQueryRs']['CustomerRet'] as $customer_data)
        {
            $customer_info = array();
            foreach($customer_data as $attribute => $info)
            {
                if ($attribute == 'BillAddressBlock') continue;

                if ($attribute == 'IsActive')
                    $info = $info == 'true' ? 1 : 0;

                if ($attribute == 'Sublevel')
                {
                    $info = (int) $info;
                    if (empty($info))
                        $info = 0;
                }

                if (is_array($info))
                {
                    foreach($info as $key => $value)
                    {
                        if (!is_array($value))
                            $customer_info[$attribute . '_' . $key] = stripslashes($value); // Magic Quotes being on, should check magic quotes before using stripslashes tho
                    }
                }
                else
                    $customer_info[$attribute] = stripslashes($info); // Magic Quotes being on, should check magic quotes before using stripslashes tho
            }

            if (!empty($customer_info))
            {
                $columns = implode(', ', array_keys($customer_info));
                $values = implode(', ', array_map('add_quotes', $customer_info));

                $sql = 'INSERT INTO quickbooks_customers (' . $columns . ') VALUES (' . $values . ')';

                // file_put_contents(dirname(__FILE__) . '/sql.txt', var_export($sql, true) . PHP_EOL, FILE_APPEND | LOCK_EX);
                // Insert this customer into the database now:
                mysql_query($sql);
            }
        }
    }
}


function _quickbooks_error_stringtoolong($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg)
{
    mail('[email protected]', 
        'QuickBooks error occured!', 
        'QuickBooks thinks that ' . $action . ': ' . $ID . ' has a value which will not fit in a QuickBooks field...');
}
?>

Not sure where to set Full Path of QBW file here. Any help would be very appreciated. My <QBType> is set to QBFS if it helps.

I am using the following to queue up actions:

$Queue = new QuickBooks_WebConnector_Queue($connection_string);
$Queue->enqueue(QUICKBOOKS_QUERY_CUSTOMER, $random_unique_id);

But How to put in the .QBW File path? Should it be going in $soap_options or $handler_options, or does it exist separate from any params that I can send with the Service? If I could just see an example of someone linking a QBW file correctly using this API, that would be most helpful. I know the Path of the .QBW file on the Server, just need to send it back as a response, and now entirely sure how to do that using Keith Palmers code, of if it's even possible to do easily within that API...?


Solution

  • Not sure where to set Full Path of QBW file here.

    Copy/pasted from our wiki:

    You can tell the framework to connect to QuickBooks even if QuickBooks is not open by setting the 'qb_company_file' field in the 'quickbooks_user' SQL table to the full path of the company file. That means that for QuickBooks financial editions (i.e. Pro, Premier, Enterprise editions), you should be doing:

    UPDATE quickbooks_user SET qb_company_file = 'C:\path\to\your\file.QBW' WHERE qb_username = 'your-web-connector-username'