WooCommerce改自动确认收货时长
很多做数字商品、虚拟充值类业务的站点,会发现 WooCommerce 默认没有直接的“自动确认收货”时间设置,订单可能在很长一段时间后仍停留在某个状态。
如果商品是虚拟物品,通常用户付款后就应该立即完成,或者只需很短的确认周期。
本文用代码方式帮你在 WooCommerce 中调整自动确认收货时长,让虚拟商品订单更快完成,同时保留实体商品的默认周期。
先把默认订单状态逻辑搞清楚
WooCommerce 对数字商品和实体商品的处理逻辑并不相同。
默认情况下,虚拟产品(virtual)在支付成功后订单会直接进入 completed 状态,而实体商品通常进入 processing 状态,需要商家手动发货或等待自动处理。
如果你安装过“自动完成订单”这类插件,那么订单从 processing 到 completed 的时间通常由插件设置决定,常见默认是 5 天。
所谓“自动确认收货时长”,就是指这一步的等待时间。
如果你没有使用插件,而是想用代码实现同样效果,就需要理解两个关键动作:
- 在订单进入
processing时触发一个定时任务; - 定时任务在设定时间后把订单改为
completed。
下面给出一份可直接放在子主题 functions.php 中的代码,按商品类型区分虚拟商品的等待时长。
用钩子和定时任务精准控制确认周期
先做准备工作:确保你的 WordPress 站点可以使用子主题,如果使用宝塔面板,可在 网站 → 根目录 → /wp-content/themes/你的主题/ 下新建 functions.php 写入代码。
建议使用子主题,避免主题更新时丢失。
以下代码实现:当订单状态进入 processing 时,判断订单里是否有虚拟商品,如果有则等待 1 天(可改)后自动完成;
否则等待 5 天(可改)后自动完成。
// 在订单进入 processing 时安排自动确认收货事件
add_action('woocommerce_order_status_processing', 'schedule_auto_complete_order', 10, 1);
function schedule_auto_complete_order($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
$has_virtual = false;
foreach ($order->get_items() as $item) {
$product = $item->get_product();
if ($product && $product->is_virtual()) {
$has_virtual = true;
break;
}
}
// 虚拟商品 1 天后确认,实体商品 5 天后确认
$delay = $has_virtual ? DAY_IN_SECONDS : 5 * DAY_IN_SECONDS;
// 清除已有相同事件,避免重复调度
wp_clear_scheduled_hook('auto_complete_order_event', array($order_id));
wp_schedule_single_event(time() + $delay, 'auto_complete_order_event', array($order_id));
}
// 执行自动完成订单动作
add_action('auto_complete_order_event', 'do_auto_complete_order');
function do_auto_complete_order($order_id) {
$order = wc_get_order($order_id);
if ($order && $order->has_status('processing')) {
$order->update_status('completed', '自动确认收货超时完成');
}
}
如果你想统一修改所有订单的自动确认时长,直接把 $delay 改成固定值即可。
例如所有订单都改为 2 天,就写成 $delay = 2 * DAY_IN_SECONDS;。
只对虚拟商品单独设置更短周期
上面代码已经做了区分:订单包含虚拟商品就使用 1 天,否则使用 5 天。
但要注意,这里的“虚拟商品”是指产品编辑页中勾选了 虚拟 属性,而不是只代表“没有实体发货”的默认判断。
如果你希望更彻底一些:订单中只要有一个虚拟商品,整单都按短周期处理。
现在的逻辑正是如此。
如果你希望只要虚拟商品订单立即完成,不等待任何时间,可以把 $delay 设为 0,但要谨慎,因为自动完成会跳过确认收货环节。
另外,如果订单中同时包含虚拟商品和实体商品,使用上面的判断逻辑会走短周期,这可能会让实体商品也跟着提前完成。
建议只让纯虚拟订单走短周期,可以这样改判断条件:
$only_virtual = true;
foreach ($order->get_items() as $item) {
$product = $item->get_product();
if ($product && !$product->is_virtual()) {
$only_virtual = false;
break;
}
}
$delay = $only_virtual ? DAY_IN_SECONDS : 5 * DAY_IN_SECONDS;
这样混合订单会走 5 天的长周期,避免实体商品被提前确认。
让配置立即生效的验证方法
代码写好并保存后,你需要模拟一个真实的订单来验证。
最稳妥的方式是:
- 在后台新建一个虚拟商品,价格设为 0.01 元;
- 用“现金支付”或其他测试支付网关下单;
- 在订单详情页把订单状态手动改为“处理中”;
- 查看数据库
wp_options表中的cron选项,或者安装 WP Crontrol 插件,检查是否出现auto_complete_order_event这个定时任务; - 如果出现,把它的执行时间临时改成 1 分钟后再测试,等待后确认订单是否自动变为“已完成”。
如果订单没有自动完成,优先检查:
- 网站是否有伪静态或缓存插件在干扰定时任务?建议关闭页面缓存后测试。
- WordPress 的
wp-cron.php是否被服务器禁用了?可以临时在wp-config.php中定义define('DISABLE_WP_CRON', true);,并在宝塔计划任务中每 5 分钟访问一次wp-cron.php。 - 代码是否真的加载?在
functions.php中写一个error_log日志观察是否被触发。
避坑与高频疑问说明
不要在主题更新后丢失代码。 建议使用子主题或代码插件(如 Code Snippets)保存这段代码,不要直接改父主题的 functions.php。
定时任务依赖站点访问。 如果网站访问量很低,wp-cron 可能迟迟不执行,自动确认就会延后。
可以通过宝塔计划任务添加每 5 分钟访问一次的规则来解决,例如访问 https://你的域名/wp-cron.php?。
doing_wp_cron
为什么虚拟商品付款后没有立刻完成? 这通常是因为商品没有勾选“虚拟”属性,或者订单状态不是 processing。
请先检查产品编辑页的 产品数据 → 虚拟 复选框,再检查订单状态变化。
代码会不会影响已经发生的订单? 不会。
代码只对每次进入 processing 状态的订单生效,已经处于其他状态的旧订单不会自动补入调度。
如果你使用的是第三方自动确认收货插件,建议直接去插件设置里找时间参数,通常会有“发货后几天自动确认”的选项,效果和上面代码一致,但配置路径更直观。
如果你在调整过程中遇到订单状态卡住或定时任务不触发,先按验证方法里的步骤排查,再回来检查代码里的订单状态限制条件。
一般能正常运行后,虚拟商品的确认周期就能明显缩短,用户体验也会更顺畅。