通过WooCommerce REST API同步站点时的订单重复问题
WooCommerce跨站点订单同步重复创建问题解决
问题根源
核心问题是缺少源订单ID与目标订单ID的映射机制,每次cron任务运行都会拉取所有源订单并重复创建;之前尝试的两种方案都存在缺陷:
- 直接复用源订单ID更新:导致订单项累加,因为PUT请求会追加订单项而非替换现有内容
- 给订单项添加源ID:目标站点的订单项ID是本地生成的,无法识别源站点的ID,因此触发
woocommerce_rest_invalid_item_id错误
解决方案步骤
1. 建立订单ID映射存储
利用WordPress的wp_options表存储源订单ID和目标订单ID的对应关系,快速判断订单是否已完成同步。
2. 优化订单拉取逻辑
通过WooCommerce API的modified_after参数,只拉取上次同步之后更新的订单,减少不必要的请求开销。
3. 区分创建/更新操作
对每个源订单,先检查映射关系:
- 存在对应目标ID:调用PUT请求更新订单,更新前先清空原有订单项再重新添加
- 不存在对应目标ID:调用POST请求创建新订单,成功后记录映射关系
4. 正确处理订单项更新
更新订单时必须先删除目标订单的所有现有订单项,再添加源订单的订单项,彻底解决累加问题。
修改后的完整代码
// Cron Schedule to Run the Code add_filter('cron_schedules', 'add_every_minute_interval'); function add_every_minute_interval($schedules) { $schedules['every_minute'] = array( 'interval' => 60, 'display' => __('Every Minute') ); return $schedules; } add_action('wp_loaded', 'schedule_order_sync'); function schedule_order_sync() { if (!wp_next_scheduled('sync_orders_event')) { wp_schedule_event(time(), 'every_minute', 'sync_orders_event'); } } add_action('sync_orders_event', 'sync_orders_from_source_to_destination'); // 获取订单ID映射 function get_order_id_mapping() { $mapping = get_option('wc_order_sync_mapping', array()); return is_array($mapping) ? $mapping : array(); } // 保存订单ID映射 function save_order_id_mapping($source_order_id, $dest_order_id) { $mapping = get_order_id_mapping(); $mapping[$source_order_id] = $dest_order_id; update_option('wc_order_sync_mapping', $mapping); } // 删除目标订单的所有订单项 function delete_all_order_items($dest_order_id, $api_url, $consumer_key, $consumer_secret) { $url = $api_url . '/wp-json/wc/v3/orders/' . $dest_order_id . '/items'; $response = wp_remote_get($url, array( 'headers' => array( 'Authorization' => 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret), ), )); if (is_wp_error($response)) return false; $items = json_decode(wp_remote_retrieve_body($response), true); if (!is_array($items)) return true; foreach ($items as $item) { $delete_url = $url . '/' . $item['id']; wp_remote_delete($delete_url, array( 'headers' => array( 'Authorization' => 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret), ), )); } return true; } // 创建或更新目标站点订单 function upsert_order_on_destination_site($source_order_id, $order_data, $api_url, $consumer_key, $consumer_secret) { $mapping = get_order_id_mapping(); // 检查订单是否已同步 if (isset($mapping[$source_order_id])) { $dest_order_id = $mapping[$source_order_id]; $url = $api_url . '/wp-json/wc/v3/orders/' . $dest_order_id; // 更新前删除所有现有订单项 delete_all_order_items($dest_order_id, $api_url, $consumer_key, $consumer_secret); // 执行更新请求 $response = wp_remote_post($url, array( 'method' => 'PUT', 'headers' => array( 'Authorization' => 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret), 'Content-Type' => 'application/json', ), 'body' => json_encode($order_data), )); } else { $url = $api_url . '/wp-json/wc/v3/orders'; // 执行创建请求 $response = wp_remote_post($url, array( 'headers' => array( 'Authorization' => 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret), 'Content-Type' => 'application/json', ), 'body' => json_encode($order_data), )); } if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $processed_order = json_decode($body, true); // 新订单创建成功后保存映射 if (!isset($mapping[$source_order_id]) && $processed_order && isset($processed_order['id'])) { save_order_id_mapping($source_order_id, $processed_order['id']); } return $processed_order; } // 从源站点拉取增量订单 function get_orders_from_source_site($api_url, $consumer_key, $consumer_secret) { $last_sync_time = get_option('wc_last_order_sync_time', '2000-01-01T00:00:00'); $url = $api_url . '/wp-json/wc/v3/orders?modified_after=' . urlencode($last_sync_time); $response = wp_remote_get($url, array( 'headers' => array( 'Authorization' => 'Basic ' . base64_encode($consumer_key . ':' . $consumer_secret), ), )); if (is_wp_error($response)) { return array(); } $body = wp_remote_retrieve_body($response); $orders = json_decode($body, true); // 更新上次同步时间为当前UTC时间 update_option('wc_last_order_sync_time', gmdate('Y-m-d\TH:i:s')); return $orders; } // 主同步函数 function sync_orders_from_source_to_destination() { // 源站点API信息 $sourceSiteApiUrl = 'https://source.com'; $sourceConsumerKey = 'xxxxxxxxxxxxxxxxxxxx'; $sourceConsumerSecret = 'xxxxxxxxxxxxxxxxxxxx'; // 目标站点API信息 $destinationSiteApiUrl = 'https://destination.com'; $destinationConsumerKey = 'xxxxxxxxxxxxxxxxxxxx'; $destinationConsumerSecret = 'xxxxxxxxxxxxxxxxxxxx'; // 拉取增量订单 $orders = get_orders_from_source_site($sourceSiteApiUrl, $sourceConsumerKey, $sourceConsumerSecret); if (!empty($orders) && is_array($orders)) { foreach ($orders as $order) { if (isset($order['id'], $order['status'], $order['customer_id'])) { $source_order_id = $order['id']; $first_name = $order['billing']['first_name']; $last_name = $order['billing']['last_name']; $phone = $order['billing']['phone']; $line_items_data = array(); if (isset($order['line_items']) && is_array($order['line_items'])) { foreach ($order['line_items'] as $item) { $line_item_data = array( 'name' => $item['name'], 'quantity' => $item['quantity'], 'price' => $item['price'], 'subtotal' => $item['subtotal'], 'total' => $item['total'], 'product_id' => $item['product_id'], ); $line_items_data[] = $line_item_data; } } // 组装同步用的订单数据 $order_data = array( 'status' => $order['status'], 'customer_id' => $order['customer_id'], 'billing' => array( 'first_name' => $first_name, 'last_name' => $last_name, 'phone' => $phone, ), 'line_items' => $line_items_data, // 可添加其他需要同步的字段(如配送信息、付款方式等) ); // 执行创建/更新操作 $processed_order = upsert_order_on_destination_site($source_order_id, $order_data, $destinationSiteApiUrl, $destinationConsumerKey, $destinationConsumerSecret); if ($processed_order) { echo (isset($mapping[$source_order_id]) ? 'Updated' : 'Created') . ' Order ' . $processed_order['id'] . ' on destination site'; } else { echo 'Failed to process order ' . $source_order_id . ' on destination site'; } } else { echo 'Order data is missing required fields'; } } } else { echo 'No orders fetched from the source site'; } }
关键修改说明
- 订单映射机制:新增
get_order_id_mapping和save_order_id_mapping函数,用wc_order_sync_mapping选项持久化存储源与目标订单的ID对应关系 - 增量拉取优化:
get_orders_from_source_site函数通过modified_after参数实现增量同步,避免每次拉取全量订单 - Upsert逻辑:
upsert_order_on_destination_site函数自动判断订单状态,分别执行创建或更新操作 - 订单项清理:更新订单前调用
delete_all_order_items删除目标订单的所有订单项,彻底解决订单项累加问题
内容的提问来源于stack exchange,提问作者Vanda Nojan - وندا نوژن
相关产品推荐
相关产品推荐

