PAYATHON 2026

Drupal 7 中 Ubercart 3.x 支付成功后使用哪个 hook?

支付老李

结论

Ubercart 3.x 提供了 hook_uc_payment_entered()。每当一笔付款被记入订单时,Ubercart 都会调用这个 hook。

需要注意的是,“付款已记入”并不表示“订单已全额支付”。分期付款、后台手工录入和部分退款等操作也可能触发该 hook,因此还要在 hook 中调用 uc_payment_balance() 检查订单余额。

实现方法

在自定义模块的 .module 文件中实现该 hook:

/**
 * Implements hook_uc_payment_entered().
 */
function MYMODULE_uc_payment_entered($order, $payment) {
  // 忽略退款、冲正等非正数付款。
  if ((float) $payment->amount <= 0) {
    return;
  }

  // 余额大于 0,说明订单尚未付清。
  if ((float) uc_payment_balance($order) > 0) {
    return;
  }

  // 订单已经付清,在这里执行后续处理。
  db_merge('mymodule_paid_order')
    ->key(array(
      'order_id' => (int) $order->order_id,
    ))
    ->insertFields(array(
      'payment_id' => (int) $payment->payment_id,
      'paid_at' => REQUEST_TIME,
    ))
    ->updateFields(array(
      'payment_id' => (int) $payment->payment_id,
    ))
    ->execute();
}

请将 MYMODULE 和 mymodule_paid_order 替换为实际的模块名和数据表名。添加 hook 后,还需要清空 Drupal 缓存,系统才能识别新的实现。

为什么还要检查订单余额

hook_uc_payment_entered() 对应的是“新增付款记录”事件,并不是只触发一次的“支付成功”事件。比如一张 100 元的订单先支付 40 元,再支付 60 元,这两笔付款都可能触发该 hook。直到第二笔付款记入后,订单余额才会变为 0。

通常可以这样判断:

if (uc_payment_balance($order) <= 0) {
  // 订单已全额支付或存在溢付。
}

如果业务不允许溢付,可以根据项目使用的金额精度,判断余额是否等于 0。

必须考虑重复回调

支付网关可能重试通知,管理员也可能重复录入或调整付款记录。如果后续处理包括发放权益、生成兑换码、增加积分或写入业务数据,就必须保证操作幂等。

可以采取以下措施:

  • 为业务表中的 order_id 建立唯一索引。
  • 使用 db_merge() 或其他支持幂等写入的方式。
  • 不要只依赖页面跳转或“支付完成”页面,因为用户可能在支付成功后直接关闭浏览器。
  • 如果需要区分付款渠道,可以检查 $payment->method。它的具体值取决于实际安装的支付模块。

Rules 方式

如果启用了 Ubercart 的 Rules 集成,也可以检查其中是否提供了“付款被记入订单”之类的事件,再为该事件配置后续动作。Ubercart 3.x 的不同小版本和扩展模块可能提供不同的 Rules 事件,因此不能默认管理界面中一定有直接表示“订单已全额付清”的事件。

即使使用 Rules,也要添加“订单余额小于或等于 0”的条件。如果需要执行复杂的数据库写入,或严格保证操作幂等,在自定义模块中实现 hook_uc_payment_entered() 通常更直接可靠。

备注:内容仅供参考。