Payment Routing

Odoo Payment Provider 为什么真正难点常在下单前:兼容路由、币种过滤与 tokenization 闸门讲透

很多文章把 payment 的重点放在 transaction state machine,但真正决定前台“能不能看到这个支付方式”的,常常是建单之前的 provider 兼容路由。本文从 payment_provider.py 的 `_get_compatible_providers()`、`_is_tokenization_required()`、`_get_validation_amount()` 出发,讲清国家、公司、金额、币种、tokenization 与 express checkout 如何一起筛掉渠道。

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

先说结论

如果你把 Odoo 支付的难点只理解成“交易成功后状态怎么变”,那你只看到了后半段。

payment_provider.py 里的 _get_compatible_providers() 告诉我们,真正决定用户能不能在前台看到某个支付方式的,往往是建单之前的一连串资格筛选

  • 这个 provider 属不属于当前公司
  • 它有没有启用、有没有发布给外部用户
  • 付款方国家是否被支持
  • 金额是否超过最大金额
  • 当前币种是否兼容
  • 这次流程是否强制 tokenization
  • 是不是 express checkout

所以一句话概括:

Odoo 的支付接入,首先是 provider 路由问题,其次才是交易状态问题。


_get_compatible_providers() 像什么

这个方法本质上就是一个支付可用性裁决器

它不是问“理论上谁能收款”,而是问:

  • 在当前公司
  • 针对当前 partner
  • 用这个金额和币种
  • 在这次支付意图下

到底有哪些 provider 真的能被拿出来给用户选。

这类设计非常平台层。因为一旦支付方式已经被前台展示,用户会默认“这条路应该能走通”。如果你把大量不兼容 provider 也展示出来,后面即使交易状态机再漂亮,用户体验依然会像坏掉了一样。

第一道闸:公司、启用状态、外部可见性

方法一开始就按公司 domain 和 state in ['enabled', 'test'] 搜 provider。

这个动作很基础,但很多定制团队会忽略它的重要性。

支付方式不是全局按钮,而是公司维度的可运营能力。尤其多公司场景下:

  • A 公司配置了 Stripe
  • B 公司只开了转账或本地收单

这不是 UI 偏好问题,而是实际资金路由与结算主体问题。

随后,若当前用户不是内部用户,provider 还必须 is_published

这又是一条常被低估的边界:

  • “已启用”不等于“对外开放”
  • 测试中、灰度中、内测中的 provider 可以存在,但不该被外部结账页看见

第二道闸:国家匹配不是装饰字段

源码接着按 partner 国家过滤 available_country_ids

这说明 available_country_ids 不是“给运营看着舒服”的元数据,而是真正参与裁决的接入条件。

为什么它这么重要?因为现实中的支付渠道往往受这些约束:

  • 只支持特定国家发卡行
  • 只允许某些地区商户主体使用
  • 某些钱包只面向指定市场

Odoo 把这种差异放在 provider 选择阶段解决,而不是让用户点进去后才报“当前地区不支持”。这就是平台层该做的事:尽早失败,最好是根本别展示。

第三道闸:最大金额判断为什么先换算成公司币种

当有币种时,源码会先把支付金额换算成公司币种,再与 provider 的 maximum_amount 比较。

这个细节非常关键。因为 provider 的运营阈值通常是按结算主体或财务主体定义的,不一定和前台支付币种一致。

如果你直接拿展示币种去比,就会遇到两个问题:

  • 同一个 provider 的上限在不同币种下不可直接比较
  • 金额阈值与公司财务口径脱节

Odoo 先转到公司币种,等于先回到“这个商户主体怎么看这笔钱”再做判断。这种思路很适合多币种支付。

第四道闸:币种兼容比你想的更早发生

接下来是 available_currency_ids

很多实施项目喜欢把币种兼容理解成:

  • 建单后交给 provider 报错
  • 或者让 provider API 自己兜底

但 Odoo 显然不这么设计。它把币种过滤前置到了 provider 选择层。

这有两个直接收益:

  1. 结账页不会把注定失败的方式展示给用户
  2. 运营团队能更清楚地看到“这个 provider 当前为什么没出现”

源码里还支持 report 记录 unavailable reason,这意味着 Odoo 不只是过滤,还尝试给出可解释的不可用性。这对排障和运营非常有价值。

Tokenization 真正的入口不是“支付成功后建 token”

很多人谈 tokenization,只盯着 transaction 完成后有没有生成 token。

但从 provider 侧源码看,更前面的关键问题是:

这次支付流程是否应该只保留支持 tokenization 的 provider。

_get_compatible_providers() 里只要:

  • force_tokenization=True
  • _is_tokenization_required(**kwargs) 返回真

就会把不支持 allow_tokenization 的 provider 直接过滤掉。

这说明 tokenization 首先是渠道准入条件,然后才是交易后处理能力。

举个最实战的例子:

  • 你要做订阅自动扣款
  • 你要做首单绑卡、后续自动续费
  • 你要做先验证支付工具、以后再扣款

这些都要求 provider 在一开始就支持 tokenization。否则你前台给了用户错误选择,后面根本接不上业务链路。

_is_tokenization_required() 为什么默认返回 False

基类默认返回 False,其实是一种很清晰的平台声明:

  • Odoo 不预设所有支付都必须 tokenization
  • 具体业务上下文要由上层模块或 provider 自己决定

这让平台具备扩展性。

有些流程只是一笔普通在线支付;有些流程却天然要求“支付关系可复用”。把这个判断抽象成可 override 方法,比把逻辑散落在控制器和前端模板里健壮得多。

Validation amount / currency 在解决什么问题

_get_validation_amount() 默认返回 0.0,而 _get_validation_currency() 会在 provider 支持币种、支付方式支持币种和公司币种之间选一个合适值。

这套设计对应的是:

  • 不是所有 tokenization 都要真实扣款
  • 有些 provider 会走 0 元验证
  • 有些则要做一笔小额验证
  • 验证币种也不一定等于用户下单币种

很多团队容易把验证交易看成“小测试单”。其实它不是测试,而是支付工具可复用性的前置建档动作

既然是平台动作,就必须把金额和币种约束明确下来,而不能交给 provider 端“临场发挥”。

Express checkout 为什么也是 provider 过滤问题

如果 is_express_checkout=True,Odoo 会进一步要求 provider allow_express_checkout

这说明 express checkout 不是简单地“把普通支付按钮换个位置”。它意味着渠道要满足另一套交互契约:

  • 能否快速拿到付款信息
  • 是否支持更短路径的确认流程
  • 是否适合在当前页面、当前上下文下直出

所以 Express 能不能开,不是产品经理拍板,而是 provider 能力与当前业务流共同决定。

实战最容易踩的 5 个坑

坑 1:把不可用 provider 也展示出来

用户一旦看到按钮,就会认为应该能付。过滤做晚了,失败体验就会直接暴露给终端用户。

坑 2:多公司场景下把 provider 当全局资源

支付配置天然跟公司主体、结算主体、法务责任边界绑定。

坑 3:把 tokenization 当交易完成后的附属功能

实际上很多业务里,它是支付渠道能否被选中的前置条件。

坑 4:忽略 validation amount / currency

绑卡、小额验证、0 元验证是否可行,都会影响你能不能安全建立可复用支付令牌。

坑 5:把 express checkout 当 UI 开关

它本质上是 provider 能力过滤,不只是前端样式变化。

结语

如果你想真正看懂 Odoo Payment,建议把视角从“状态机如何收尾”往前挪一步,看渠道如何在交易创建前被筛选和解释

因为平台层最重要的能力,往往不是把已经开始的交易处理好,而是让不该开始的交易根本不要开始。

_get_compatible_providers() 做的,正是这件事。

DISCUSSION

评论区

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