企业|框架

Odoo 企业版官网预约支付,为什么不是“控制器里调一下 payment.transaction”

从 public 路由、invoice token 到 post-payment 回跳,讲清预约支付链为什么要靠控制器、模型与支付页多层护栏。

企业 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

结论先行

很多人做 Odoo 支付扩展时,第一反应是“拿到 appointment_type_id 后调一笔 transaction 不就完了”。企业版预约支付的代码恰好反着来:它尽量避免让浏览器直接决定任何关键状态,而是把 booking、invoice、payment page、post-payment redirect 拆成多层护栏。

第一层:入口或表面动作

第一层护栏是 public 控制器并不直接创建 event。预约页面提交后,控制器只负责创建 calendar.booking,再通过 _redirect_to_payment() 把 invoice id、partner id、amount 和 access token 拼进支付页 URL。这里的 token 来自 payment_utils.generate_access_token(...),意味着支付页读取的是一份带签名的支付上下文,不是前端随便带几个 id 就能重放。

第二层:真正的业务护栏

第二层护栏在支付返回。appointment_post_payment() 并不接受“这笔 booking 已支付”的宣告,而是先用 invoice_token 反查 account.move,再顺着 calendar_booking_ids 找 booking。只有当 booking 确实属于这张 invoice,后续跳转才成立。这层看起来啰嗦,实质上是在防止 public 路由被拼接参数撞开。

第三层:状态落点与边界

第三层护栏在模型层。account_move._post() 覆写后会调用 posted.calendar_booking_ids._make_event_from_paid_booking();而后者又会先 _filter_unavailable_bookings(),最后才建 calendar event。也就是说,就算付款这一步在支付框架里已经成功了,预约系统仍然保留一次落地前复核,避免并发下两个请求占到同一个资源时段。

为什么这套设计更稳

这种设计体现的是 Odoo 框架一贯思路:控制器负责拿 request/response、模型负责真状态、支付页负责 transaction 协调,任何 public 链路都需要 token 反查,而不是相信前端缓存。你在自定义时如果把 event 创建塞回 controller,短期能跑,长期就会遇到所有典型问题:重复提交、pending payment 重放、slot collision、退款后回跳异常。

实战启示

更值得注意的是,这条链并没有让 payment 模块知道 appointment 的业务细节;支付框架只扩展额外 rendering values 和 landing route,业务最终解释权还在 booking/invoice/event 模型里。这正是“框架”文章里最该学的东西:跨模块集成最稳定的方式,不是把业务塞进支付通道,而是让支付成为一个经过验证的中继点。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。