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() 通常更直接可靠。
备注:内容仅供参考。
