Prestashop 支付验证后如何更改订单状态
结论
actionOrderStatusPostUpdate 中的“PostUpdate”表示订单状态的切换逻辑已经执行,但对应的 OrderHistory 记录未必已经写入完成。
在常见的 PrestaShop 订单状态处理流程中,这个 Hook 触发后,外层代码可能还会继续保存原有的 "Payment validated" 历史记录,并更新订单的当前状态。如果在 Hook 内立即调用 setCurrentState(),就会产生嵌套的状态变更:自定义状态先写入,随后外层流程又写入付款成功状态,最终将自定义状态覆盖。
更合适的做法是等订单历史记录写入完成后再处理,例如使用 actionOrderHistoryAddAfter。当刚写入的状态是 PS_OS_PAYMENT 时,再追加自定义状态。
为什么会出现顺序颠倒
订单状态变更通常包含两个相关但不同步的动作:
- 执行新状态对应的业务逻辑。
- 向
order_history写入状态记录,并更新订单的current_state。
actionOrderStatusPostUpdate 位于状态切换流程内部。此时调用:
$oOrder->setCurrentState($targetStatusId);
会立即发起另一次状态变更,并写入 "My personnal status"。
Hook 返回后,原来的付款验证流程仍会继续,并在随后保存 "Payment validated"。实际写入顺序因此变成:
My personnal status
Payment validated
最后写入的 "Payment validated" 也就成了订单的当前状态。
setCurrentState() 本身会创建订单历史记录并保存当前状态,通常不需要再调用:
$oOrder->save();
推荐实现
先确认当前 PrestaShop 版本提供 actionOrderHistoryAddAfter,然后注册并实现该 Hook:
public function hookActionOrderHistoryAddAfter($params)
{
if (empty($params['order_history'])) {
return;
}
/** @var OrderHistory $orderHistory */
$orderHistory = $params['order_history'];
$paymentStatusId = (int) Configuration::get('PS_OS_PAYMENT');
$targetStatusId = (int) Configuration::get('XXXXXX_STATUS_WAITING');
// 只处理刚刚写入的 Payment validated 状态
if ((int) $orderHistory->id_order_state !== $paymentStatusId) {
return;
}
if ($targetStatusId <= 0) {
return;
}
$order = new Order((int) $orderHistory->id_order);
if (!Validate::isLoadedObject($order)) {
return;
}
// 防止重复写入
if ((int) $order->current_state === $targetStatusId) {
return;
}
$order->setCurrentState($targetStatusId);
}
安装模块时还要注册该 Hook:
public function install()
{
return parent::install()
&& $this->registerHook('actionOrderHistoryAddAfter');
}
如果模块已经安装,只修改 install() 不会自动注册 Hook。此时需要重新安装模块,或者通过模块升级脚本注册:
function upgrade_module_1_0_1($module)
{
return $module->registerHook('actionOrderHistoryAddAfter');
}
执行后的状态顺序
付款状态的历史记录保存完成后,Hook 才会追加自定义状态,结果应为:
Current status : My personnal status
History :
My personnal status
Payment validated
后台通常会按时间倒序显示历史记录,因此最新的自定义状态位于最上方。
注意事项
setCurrentState() 会再次写入一条 OrderHistory,也可能再次触发与订单历史有关的 Hook。示例中的以下检查:
$orderHistory->id_order_state !== $paymentStatusId
会忽略自定义状态产生的第二次回调,避免递归。
还需注意:
- 不要在
setCurrentState()后重复调用$order->save()。 - 确认
XXXXXX_STATUS_WAITING返回的是有效的OrderStateID。 - 如果目标状态配置了发送邮件、生成发票或其他业务动作,调用
setCurrentState()时也可能执行这些动作,需要检查该状态在后台的配置。 - 不同 PrestaShop 主版本的 Hook 名称和触发位置可能不同。如果当前版本没有
actionOrderHistoryAddAfter,应检查该版本OrderHistory类的实际处理流程,选择在历史记录持久化之后触发的 Hook,不要继续在actionOrderStatusPostUpdate中嵌套修改状态。
备注:内容仅供参考。
