You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QuickBooks Desktop与PHP集成问题:重复创建客户循环

解决QuickBooks Web Connector无限重复创建客户的问题

兄弟,你这代码的问题太明显了——每次Web Connector调用这个脚本时,你都无条件地执行了$Queue->enqueue(QUICKBOOKS_ADD_CUSTOMER, $primary_key_of_your_customer),这就导致Web Connector每轮询一次(默认几分钟一次),就把同一个添加客户的任务重新塞进队列,然后它就会不断处理这个任务,无限创建新客户,直到你用die()强制终止。

核心解决方案:只在客户未同步时入队

你需要给入队逻辑加个判断:只有当这个客户还没被同步到QuickBooks时,才把任务加入队列。具体来说,你需要在数据库里给客户表加个标记(比如quickbooks_listid字段,为空表示未同步),然后:

  1. 脚本运行时先检查该客户的同步状态
  2. 仅当未同步时,才调用enqueue()
  3. 同步成功后,在回调函数里把QuickBooks返回的ListID保存到数据库,标记为已同步

修改后的完整代码示例

<?php 
$primary_key_of_your_customer = 5; 
require_once '../../QuickBooks.php'; 

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

$map = array(
    QUICKBOOKS_ADD_CUSTOMER => array( 
        '_quickbooks_customer_add_request', 
        '_quickbooks_customer_add_response'
    )
); 

$errmap = array( 3070 => '_quickbooks_error_stringtoolong'); 
$hooks = array(); 
$log_level = QUICKBOOKS_LOG_DEBUG; 
$soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN; 
$soap_options = array(); 
$handler_options = array('deny_concurrent_logins' => false, 'deny_reallyfast_logins' => false); 
$driver_options = array(); 
$callback_options = array(); 

$dsn = 'mysqli://root:password@localhost/quickbooks'; 

// 1. 先检查客户是否已经同步到QuickBooks
// 假设你的客户表叫customers,有quickbooks_listid字段(空值=未同步)
$db_conn = new mysqli('localhost', 'root', 'password', 'quickbooks');
$check_query = $db_conn->query("SELECT quickbooks_listid FROM customers WHERE id = " . $primary_key_of_your_customer);
$customer_data = $check_query->fetch_assoc();

$Queue = new QuickBooks_WebConnector_Queue($dsn);
// 2. 仅当客户未同步时,才加入队列
if (empty($customer_data['quickbooks_listid'])) {
    $Queue->enqueue(QUICKBOOKS_ADD_CUSTOMER, $primary_key_of_your_customer);
}

// 初始化Web Connector服务并处理请求
$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) { 
    // 从数据库读取真实的客户数据,不要硬编码
    $db_conn = new mysqli('localhost', 'root', 'password', 'quickbooks');
    $customer_query = $db_conn->query("SELECT * FROM customers WHERE id = " . $ID);
    $customer = $customer_query->fetch_assoc();

    // 生成QBXML请求(注意用htmlspecialchars转义防止XML错误)
    $xml = '<?xml version="1.0" encoding="utf-8"?> 
    <?qbxml version="2.0"?> 
    <QBXML> 
        <QBXMLMsgsRq onError="stopOnError"> 
            <CustomerAddRq requestID="' . $requestID . '"> 
                <CustomerAdd> 
                    <Name>' . htmlspecialchars($customer['name']) . '</Name> 
                    <CompanyName>' . htmlspecialchars($customer['company_name']) . '</CompanyName> 
                    <FirstName>' . htmlspecialchars($customer['first_name']) . '</FirstName> 
                    <LastName>' . htmlspecialchars($customer['last_name']) . '</LastName> 
                    <BillAddress> 
                        <Addr1>' . htmlspecialchars($customer['bill_addr1']) . '</Addr1> 
                        <Addr2>' . htmlspecialchars($customer['bill_addr2']) . '</Addr2> 
                        <City>' . htmlspecialchars($customer['bill_city']) . '</City> 
                        <State>' . htmlspecialchars($customer['bill_state']) . '</State> 
                        <PostalCode>' . htmlspecialchars($customer['bill_zip']) . '</PostalCode> 
                        <Country>' . htmlspecialchars($customer['bill_country']) . '</Country> 
                    </BillAddress> 
                    <Phone>' . htmlspecialchars($customer['phone']) . '</Phone> 
                    <Email>' . htmlspecialchars($customer['email']) . '</Email> 
                    <Contact>' . htmlspecialchars($customer['contact_name']) . '</Contact> 
                </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) { 
    // 3. 同步成功后,保存QuickBooks返回的ListID到数据库,标记为已同步
    $db_conn = new mysqli('localhost', 'root', 'password', 'quickbooks');
    // 用real_escape_string防止SQL注入,更好的方式是用预处理语句
    $list_id = $db_conn->real_escape_string($idents['ListID']);
    $db_conn->query("UPDATE customers SET quickbooks_listid = '$list_id', quickbooks_sync_at = NOW() WHERE id = " . $ID);
}

额外注意事项

  • 别再硬编码客户信息了,从数据库读取真实数据才是同步的意义,不然每次都生成随机的ConsoliBYTE客户完全没用。
  • 一定要在_quickbooks_customer_add_response里保存ListID,后续更新客户、创建关联单据都需要用到这个ID。
  • 数据库操作要注意安全,上面的示例用了real_escape_string,但更推荐用预处理语句来彻底避免SQL注入。
  • Web Connector的轮询间隔可以在QBWC的设置里调整,但核心问题还是你之前无条件入队的逻辑,解决这个就不会无限循环了。

内容的提问来源于stack exchange,提问作者Mohit Navik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 09:12:33