QuickBooks Desktop与PHP集成问题:重复创建客户循环
解决QuickBooks Web Connector无限重复创建客户的问题
兄弟,你这代码的问题太明显了——每次Web Connector调用这个脚本时,你都无条件地执行了$Queue->enqueue(QUICKBOOKS_ADD_CUSTOMER, $primary_key_of_your_customer),这就导致Web Connector每轮询一次(默认几分钟一次),就把同一个添加客户的任务重新塞进队列,然后它就会不断处理这个任务,无限创建新客户,直到你用die()强制终止。
核心解决方案:只在客户未同步时入队
你需要给入队逻辑加个判断:只有当这个客户还没被同步到QuickBooks时,才把任务加入队列。具体来说,你需要在数据库里给客户表加个标记(比如quickbooks_listid字段,为空表示未同步),然后:
- 脚本运行时先检查该客户的同步状态
- 仅当未同步时,才调用
enqueue() - 同步成功后,在回调函数里把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
相关产品推荐
相关产品推荐

