discussion-record.md 19 KB

OMG 支付方案讨论记录

记录信息

  • 会话 ID:019fe942-4fb3-7701-b8d6-70f141f7774d
  • 讨论日期:2026-08-10
  • 讨论主题:OMG 多店铺、夜市摊位、平台商模式、LINE Pay 扩展及店铺端收款设置
  • 名称说明:早期讨论中多次写作 OGM,实际支付渠道名称为 OMG

当前最终决策

经过完整讨论后,当前阶段采用最简单的店铺级支付配置方案:

  1. Foodie 平台不代收、不分账,款项由支付渠道直接结算给商户。
  2. 支付设置直接放在店铺管理中,不额外抽象通用的支付账户和店铺绑定体系。
  3. OMG 继续使用现有店铺级配置表 pos_store_omg;以后接入 LINE Pay 时,可增加店铺级的 pos_store_linepay
  4. 暂不实现 PlatformID、平台商、子商户、平台结算主体及二次结算逻辑。
  5. 多店铺场景较少,不为少量场景预先增加复杂架构。
  6. 如果少量多店铺需要共用同一个 OMG 商户:各店保存相同的 MerchantID / HashKey / HashIV,使用不同的 OMG StoreID;回调通过 MerchantTradeNo 定位订单和店铺。
  7. HashKey / HashIV / Channel Secret 等敏感凭证必须加密保存,管理页面只显示掩码。

当前简化关系:

店铺
  └─ 店铺收款设置
       ├─ OMG 配置
       └─ LINE Pay 配置(后续)

支付渠道
  └─ 款项直接结算给商户

Foodie
  ├─ 发起支付
  ├─ 校验支付回调
  ├─ 更新订单状态
  └─ 提供查询、退款等技术功能

第一轮:一个商户有多个店铺时,OMG 如何区分

用户问题

ogm 支付不能再后台自己管理店铺新增多个店铺吗?我们的系统现在一个商户可能会有多个店铺,ogm怎么区分

分析过程

  • 检查项目现有 OMG 支付实现和数据库设计。
  • 确认当前代码按 pos_store.id 读取 OMG 凭证。
  • 确认当前回调按 OMG MerchantID 反查店铺。
  • 核对 OMG 是否允许同一 MerchantID 下使用多个门店标识。

结论

OMG 提供 StoreID 区分同一商户旗下的多个店铺:

  • MerchantID:OMG 特店编号,代表收款商户或结算主体。
  • StoreID:该特店旗下的分店代号,最长 20 位,只能使用英文字母和数字。
  • 支付成功回调会原样返回 StoreID,可用于核对订单所属门店。

官方文档:

示例:

商户 user_id = 88
OMG MerchantID = 2000132

门店 1001 → StoreID = S1001
门店 1002 → StoreID = S1002
门店 1003 → StoreID = S1003

发起门店 1002 的订单时:

MerchantID=2000132
StoreID=S1002
MerchantTradeNo=OMGxxxxxxxxxxxxxxxxx

当时发现的当前实现问题:

  • 下单只传 MerchantID,没有传 StoreID
  • pos_store_omg 按门店保存整套凭证。
  • merchant_id 设置了唯一索引。
  • 回调直接使用 MerchantID 反查唯一门店。

因此,当时的实现实际上强制“一店一个 MerchantID”。如果同一商户的多个门店共用一个 MerchantID,第二个门店会遇到唯一索引冲突。

当时建议的完整模型是“一商户一套 OMG 凭证,多门店各自使用一个 StoreID”;最终方案后来根据业务规模进一步简化为店铺级配置。


第二轮:StoreID 是否需要在 OMG 后台管理

用户问题

但是omg 后台管理没有店铺管理,传人storeId是可以自定义吗?还是需要在omg 那边管理?

分析过程

  • 核对 OMG 官方文档中 StoreID 的填写与回传规则。
  • 重点确认是否需要预先在 OMG 后台登记门店。
  • 区分“交易归属标识”与“独立结算账户”。

结论

StoreID 可以由 Foodie 自行定义并随订单传入,不需要先在 OMG 后台新增门店。

OMG 对 StoreID 的限制:

  • 最长 20 位。
  • 只能使用大小写英文字母和数字。
  • 付款回调会原样返回 StoreID

官方文档没有说明需要预先登记,也没有提供新增门店 API。

建议生成方式:

pos_store.id = 123
OMG StoreID = S123

发起支付:

MerchantID=2000132
StoreID=S123
MerchantTradeNo=OMGxxxxxxxxxxxxxxxxx

需要注意:StoreID 只是交易归属标识,不是 OMG 的独立子商户账户。使用同一个 OMG 商户账户的所有店铺仍然:

  • 共用 MerchantID / HashKey / HashIV
  • 共用结算账户和费率。
  • 在 OMG 层属于同一个特店。

如果要求每个门店分别结算或绑定不同银行账户,则需要各自申请 MerchantID,不能只依赖 StoreID

OMG 后台是否支持按 StoreID 筛选或导出报表,公开文档没有保证,需要向 OMG 商务确认;但支付接口本身支持使用 StoreID 区分门店。


第三轮:平台商和子商户模式是什么

用户问题

平台商/子商户模式又是什么

分析过程

  • 按 OMG 对 PlatformID 的定义,区分平台商模式和普通的 MerchantID + StoreID 模式。
  • 讨论代收、结算、商户审核和平台责任。
  • 确认不能只根据接口字段推断平台是否具有代收或分账权限。

结论

平台商/子商户模式面向 Foodie 这种聚合多个独立商家的平台,不等同于同一商户的多门店。

可以理解为三级:

层级 OMG 字段 示例
Foodie 平台 PlatformID Foodie
独立餐饮商户 MerchantID 王记餐饮公司
商户旗下门店 StoreID 台北店、桃园店

支付请求示例:

PlatformID = Foodie 的平台商编号
MerchantID = 王记餐饮的 OMG 特店编号
StoreID = 王记台北店的自定义门店代号

字段含义:

  • PlatformID:OMG 审核后提供给专案合作平台,不能自行生成;普通特店应留空。
  • MerchantID:每个独立收款商户的特店编号。
  • StoreID:商户内部自行定义的门店标识,不是独立收款账户。

示例:

Foodie PlatformID = P00001

商户 A:
  MerchantID = M10001
  门店 A1 → StoreID = S101
  门店 A2 → StoreID = S102

商户 B:
  MerchantID = M10002
  门店 B1 → StoreID = S201

平台商模式通常需要:

  • Foodie 与 OMG 签约并开通平台商资格。
  • OMG 分配 PlatformID
  • 每个子商户按照合作方案开户、审核、绑定银行卡并取得 MerchantID
  • 分账、直接结算和平台抽佣能力需要 OMG 通过合同和接口明确支持。

如果只是一个商户经营多个门店,使用 MerchantID + StoreID 即可,不需要平台商模式。

最终业务已明确 Foodie 不代收、不分账,因此当前阶段不采用平台商/子商户模式。


第四轮:夜市每个摊位是否都需要申请商户

用户问题

现在有个问题是,夜市有很多摊位,每个摊位也要也能申请一个商户吗?开一个商户会不会门槛比较高?

分析过程

  • 将问题从接口字段提升到实际收款主体和结算责任。
  • 核对 OMG 当时公开的申请材料、审核要求和费用。
  • 对比摊位独立收款、夜市统一收款以及平台子商户三种方案。

结论

如果每个摊位由不同老板经营,并且款项需要进入不同银行账户,那么每个摊位原则上应使用独立 OMG 商户账户。

OMG 当时公开支持自然人和公司户申请,但仍需提交收款资料并经过审核,不是注册账号后就能自动开通。

当时查询到的公开费用(截至 2026-08-10):

  • 设置费:NT$1,000/次。
  • 年费:NT$5,000/年。
  • 信用卡/Apple Pay:2.95%,最低 NT$5/笔。
  • ATM:1%,最低 NT$15/笔。
  • 超商代码:NT$38/笔。
  • 30 日收款额度:最高 NT$200,000。

相关资料:

100 个独立摊位按公开价格各开一个普通商户的示例:

首年固定费用:
100 × (1,000 + 5,000) = NT$600,000

以后每年:
100 × 5,000 = NT$500,000

因此,大量小摊位逐个开户的经济和运营门槛较高。

讨论中曾考虑:

夜市场景 方案
所有摊位由同一家公司经营、统一收款和报税 一个 MerchantID,不同摊位使用不同 StoreID
每个摊位是独立老板,款项直接给各摊位 每摊位独立 MerchantID
每个摊位独立,平台统一技术接入和批量开户 与 OMG 商谈 PlatformID + 子商户
夜市统一代收后再给摊位结算 必须取得 OMG 对代收、分账方案的明确认可

最终业务已明确 Foodie 不参与收款。因此平台不会使用自己的 MerchantID 代收再结算;每个店铺或摊位应绑定最终实际收款商户的支付配置。


第五轮:为了灵活性,支付信息是否应该放在店铺管理

用户问题

现在使用哪种商户方案没不能确定,为了保持最好的灵活度,支付信息是不是还是应该放在店铺来管理?

分析过程

  • 区分“门店操作入口”和“支付凭证归属”。
  • 评估当前 pos_store_omg 是否会把系统限制为“一店一 MerchantID”。
  • 研究通过支付账户和门店绑定关系支持多种商户方案的可能性。

当时结论

当时建议把数据拆为两层:

OMG支付账户 pos_omg_account
  ├─ 账户模式
  ├─ PlatformID
  ├─ MerchantID
  ├─ HashKey/HashIV
  ├─ 所属收款主体
  └─ 申请、审核、环境、状态

门店绑定 pos_store_omg
  ├─ store_id
  ├─ omg_account_id
  ├─ omg_store_id
  ├─ is_enabled
  └─ enabled_payments

该设计可以覆盖:

一店一商户:
门店A → OMG账户A

一个商户多店:
门店A ─┐
门店B ─┼→ 同一个OMG账户,不同StoreID
门店C ─┘

平台子商户:
门店A → 子商户账户A
门店B → 子商户账户B

当时形成的原则是:门店管理支付开关和账户绑定,支付账户管理商户身份、结算关系和敏感凭证。

后续取舍

在明确 Foodie 不代收且多店铺场景较少之后,决定不实施上述账户绑定抽象。当前阶段直接使用店铺级 pos_store_omg 配置,优先降低实现和操作复杂度。


第六轮:普通商家店铺与夜市摊位的归属差异

用户问题

但是摊位的时候,用户关联摊位的逻辑跟商家用户关联店铺的逻辑不太一样。你先看看他们之间的局别

分析过程

  • 只分析现有关系,不修改代码。
  • 对比普通商家、夜市和摊位主的用户类型、关联字段、创建流程、查询权限和订单归属。
  • 核对摊位创建、摊位主管理、钱包和订单入账代码。

结论

普通商家和摊位是两套不同的归属模型,不能统一通过 pos_store.user_id 判断实际收款主体。

对比项 普通商家店铺 夜市摊位
用户类型 商家 userType=1 夜市 userType=3、摊位主 userType=4
店铺归属 PosStore.user_id = 商家ID PosStore.user_id = 夜市ID
摊位标识 is_stall=0 is_stall=1
操作账号 商家本人管理多个店铺 多个摊位主共同管理一个摊位
操作账号关联 用户 → 多店铺 InfoUser.store_id → 摊位ID
钱包归属 UserWallet.user_id = 商家ID UserWallet.store_id = 摊位ID
收入主体 商家用户 摊位本身

普通商家关系:

InfoUser(type=1)
  └─ 1:N PosStore(user_id=商家ID)
       └─ 收入进入商家用户钱包

夜市摊位关系:

InfoUser(type=3,夜市)
  └─ 1:N PosStore(is_stall=1,user_id=夜市ID)
       ├─ 1:N InfoUser(type=4,store_id=摊位ID)
       └─ 1:1 UserWallet(store_id=摊位ID)

关键结论:

对普通店铺,pos_store.user_id 是商家;对摊位,pos_store.user_id 是夜市管理者,不一定是实际摊位收款人。

一个摊位可以有多个 userType=4 账号,目前系统没有标记哪个账号是法人、OMG 申请人或结算负责人。

在当前简化方案下,支付配置仍直接挂在 store_id,由店铺或摊位明确填写最终实际收款商户的 OMG 凭证,不再通过 pos_store.user_id 自动推断支付商户身份。


第七轮:后续接入 LINE Pay 是否有足够灵活度

用户问题

还要考虑后续接入line pay,这样是否对于接入新的支付存在灵活度

分析过程

  • 将 OMG 和后续 LINE Pay 放入统一支付域模型进行评估。
  • 区分支付渠道 provider 与支付方式 payment_method
  • 确认当前代码中的 LINEPAY 是蓝新金流提供的付款选项,不是 LINE Pay 直连。
  • 核对 LINE Pay 的 Channel IDChannel Secret、HMAC 签名和交易流程。

当时结论

当时建议设计四层通用支付模型:

店铺/摊位
   ↓
store_payment_binding
   ↓
payment_account
   ↓
OMG / LINE Pay 渠道凭证与适配器

订单
   ↓
payment_attempt

该模型可支持:

  • 一个普通商户账户绑定多个店铺。
  • 一个夜市账户绑定多个摊位。
  • 某个摊位单独切换支付账户。
  • 同一个店铺同时启用 OMG 和 LINE Pay。
  • 不同渠道使用不同结算主体。
  • 将来新增其他支付渠道。

LINE Pay 相关资料:

后续取舍

由于当前已明确:

  • Foodie 不代收、不分账。
  • 多店铺场景较少。
  • 当前首要目标是操作和实现简单。

因此暂不建设通用四层支付模型。OMG 继续使用店铺级 pos_store_omg;将来真正接入 LINE Pay 时新增店铺级 pos_store_linepay,并在出现第三个以上支付渠道或大量账户复用需求时,再评估抽象公共支付模型。


第八轮:店铺进入收款设置后如何简单操作

用户问题

我还是不够明白,通过店铺进入管理入口,应该是怎么操作的,操作起来应该简单明了

分析过程

  • 隐藏底层账户、渠道、绑定和技术凭证概念。
  • 从门店经营者角度,只展示支付方式、收款归属和开通状态。
  • 将操作流程收敛为店铺列表入口和收款设置页面。

结论

当时形成的界面原则:

从用户角度,支付属于店铺;底层如何保存和调用凭证由系统处理。

店铺列表入口:

店铺列表
┌──────────┬────────┬──────────┐
│ 店铺名称  │ 营业状态 │ 操作       │
├──────────┼────────┼──────────┤
│ 王记一店  │ 营业中   │ 编辑|收款设置 │
│ 王记二店  │ 营业中   │ 编辑|收款设置 │
└──────────┴────────┴──────────┘

点击“收款设置”进入当前店铺的收款页面:

王记一店 · 收款设置

当前状态:正常收款
收款商户:王记餐饮有限公司
已开通:OMG

支付渠道卡片:

┌─────────────────────────────┐
│ OMG                          │
│ 状态:已开通                  │
│ 收款商户:王记餐饮有限公司      │
│ [编辑配置] [停用]              │
└─────────────────────────────┘

┌─────────────────────────────┐
│ LINE Pay                     │
│ 状态:未开通                  │
│ [开通 LINE Pay]               │
└─────────────────────────────┘

普通商户不需要看到 payment_account_idholder_type 等内部字段。MerchantIDStoreIDChannel ID 等可以放入“技术信息”区域或仅允许平台运营人员编辑。

页面只需让用户明确三件事:

  1. 当前店铺能使用哪些支付方式。
  2. 款项直接结算给哪个商户。
  3. 当前渠道是否已开通。

第九轮:确认平台不参与收款后是否需要简化

用户问题

这个会不会太复杂了,现在明确平台不管收款,支付直接进入商户

讨论结论

确认此前包含平台商、子商户、平台结算主体和通用支付账户的设计对于当前范围过于复杂。

业务关系简化为:

店铺
  ↓ 配置支付渠道
OMG / LINE Pay
  ↓ 直接结算
商户银行账户

Foodie 只负责技术处理,不接触结算资金:

  • 发起支付。
  • 接收并验证回调。
  • 更新订单支付状态。
  • 提供交易查询和退款能力。

因此暂不实现:

  • PlatformID
  • 平台商/子商户模型。
  • 平台代收和分账。
  • 平台资金账户。
  • 二次结算逻辑。

夜市摊位遵循同一原则:摊位绑定谁的支付商户账户,支付渠道就直接把钱结算给谁,Foodie 不参与后续款项再分配。


第十轮:最终选择店铺级简单配置

用户决定

我们还是简单一点直接在店铺设置就行了,多店铺的情况也不多

最终结论

当前阶段不建立独立的 payment_accountstore_payment_binding 或通用支付域模型,直接按店铺配置。

OMG 使用:

pos_store_omg
- store_id
- merchant_id
- omg_store_id
- hash_key
- hash_iv
- enabled_payments
- is_enabled
- status

管理入口:

店铺管理 → 收款设置 → OMG

少量多店铺需要共用同一个 OMG 商户时:

  • 每个店铺分别保存相同的 MerchantID / HashKey / HashIV
  • 每个店铺使用不同的 OMG StoreID
  • 数据库不能继续强制 merchant_id 全局唯一。
  • 回调必须通过 MerchantTradeNo 找到订单和店铺,不能只使用 MerchantID 反查唯一店铺。

后续接入 LINE Pay 时,再增加店铺级 pos_store_linepay 配置;不为尚未发生的大规模支付账户复用需求提前增加抽象。

实施时必须保留的安全要求

即使采用简单方案,以下要求仍不能省略:

  1. HashKey / HashIV / Channel Secret 加密保存,前端和普通管理接口只显示掩码。
  2. 支付回调必须校验签名、金额、订单状态和渠道商户编号。
  3. 支付流水记录当次使用的 store_idMerchantIDStoreID 和渠道交易编号。
  4. 回调以 MerchantTradeNo 或平台唯一支付流水定位订单,不能依赖 MerchantID 与店铺一对一。
  5. 重复回调必须幂等处理,不能重复更新订单或触发重复业务动作。

后续重新评估架构的触发条件

只有出现以下任一情况时,再考虑抽象支付账户和店铺绑定模型:

  • 大量商户需要多个店铺共用同一套支付凭证。
  • 同一店铺需要同时管理多个独立结算账户。
  • 接入三个以上直连支付渠道,店铺级表出现大量重复逻辑。
  • Foodie 的业务模式改变,需要平台商、子商户、代收或分账。
  • 凭证管理需要统一迁移到外部密钥管理服务。