# 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` 等敏感凭证必须加密保存,管理页面只显示掩码。 当前简化关系: ```text 店铺 └─ 店铺收款设置 ├─ OMG 配置 └─ LINE Pay 配置(后续) 支付渠道 └─ 款项直接结算给商户 Foodie ├─ 发起支付 ├─ 校验支付回调 ├─ 更新订单状态 └─ 提供查询、退款等技术功能 ``` --- ## 第一轮:一个商户有多个店铺时,OMG 如何区分 ### 用户问题 > ogm 支付不能再后台自己管理店铺新增多个店铺吗?我们的系统现在一个商户可能会有多个店铺,ogm怎么区分 ### 分析过程 - 检查项目现有 OMG 支付实现和数据库设计。 - 确认当前代码按 `pos_store.id` 读取 OMG 凭证。 - 确认当前回调按 OMG `MerchantID` 反查店铺。 - 核对 OMG 是否允许同一 `MerchantID` 下使用多个门店标识。 ### 结论 OMG 提供 `StoreID` 区分同一商户旗下的多个店铺: - `MerchantID`:OMG 特店编号,代表收款商户或结算主体。 - `StoreID`:该特店旗下的分店代号,最长 20 位,只能使用英文字母和数字。 - 支付成功回调会原样返回 `StoreID`,可用于核对订单所属门店。 官方文档: - [OMG 产生订单文档](https://developers-stage.omg.com.tw/payment/aio/api/01_order.html) - [OMG 付款通知文档](https://developers-stage.omg.com.tw/payment/aio/api/03_payment_notify.html) 示例: ```text 商户 user_id = 88 OMG MerchantID = 2000132 门店 1001 → StoreID = S1001 门店 1002 → StoreID = S1002 门店 1003 → StoreID = S1003 ``` 发起门店 1002 的订单时: ```text 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。 建议生成方式: ```text pos_store.id = 123 OMG StoreID = S123 ``` 发起支付: ```text 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` | 台北店、桃园店 | 支付请求示例: ```text PlatformID = Foodie 的平台商编号 MerchantID = 王记餐饮的 OMG 特店编号 StoreID = 王记台北店的自定义门店代号 ``` 字段含义: - `PlatformID`:OMG 审核后提供给专案合作平台,不能自行生成;普通特店应留空。 - `MerchantID`:每个独立收款商户的特店编号。 - `StoreID`:商户内部自行定义的门店标识,不是独立收款账户。 示例: ```text 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。 相关资料: - [OMG 注册页面](https://www.funpoint.com.tw/member/register) - [OMG 收款服务规范](https://www.funpoint.com.tw/terms/provisionlogistics) - [OMG 费用说明](https://www.funpoint.com.tw/info/fee) 100 个独立摊位按公开价格各开一个普通商户的示例: ```text 首年固定费用: 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”。 - 研究通过支付账户和门店绑定关系支持多种商户方案的可能性。 ### 当时结论 当时建议把数据拆为两层: ```text OMG支付账户 pos_omg_account ├─ 账户模式 ├─ PlatformID ├─ MerchantID ├─ HashKey/HashIV ├─ 所属收款主体 └─ 申请、审核、环境、状态 门店绑定 pos_store_omg ├─ store_id ├─ omg_account_id ├─ omg_store_id ├─ is_enabled └─ enabled_payments ``` 该设计可以覆盖: ```text 一店一商户: 门店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` | | 收入主体 | 商家用户 | 摊位本身 | 普通商家关系: ```text InfoUser(type=1) └─ 1:N PosStore(user_id=商家ID) └─ 收入进入商家用户钱包 ``` 夜市摊位关系: ```text 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 ID`、`Channel Secret`、HMAC 签名和交易流程。 ### 当时结论 当时建议设计四层通用支付模型: ```text 店铺/摊位 ↓ store_payment_binding ↓ payment_account ↓ OMG / LINE Pay 渠道凭证与适配器 订单 ↓ payment_attempt ``` 该模型可支持: - 一个普通商户账户绑定多个店铺。 - 一个夜市账户绑定多个摊位。 - 某个摊位单独切换支付账户。 - 同一个店铺同时启用 OMG 和 LINE Pay。 - 不同渠道使用不同结算主体。 - 将来新增其他支付渠道。 LINE Pay 相关资料: - [LINE Pay 官方凭证说明](https://developers-pay.line.me/online/prerequisites) - [LINE Pay Online API v4](https://developers-pay.line.me/online-api-v4) - [LINE Pay 官方变更记录](https://developers-pay.line.me/api-change-log) ### 后续取舍 由于当前已明确: - Foodie 不代收、不分账。 - 多店铺场景较少。 - 当前首要目标是操作和实现简单。 因此暂不建设通用四层支付模型。OMG 继续使用店铺级 `pos_store_omg`;将来真正接入 LINE Pay 时新增店铺级 `pos_store_linepay`,并在出现第三个以上支付渠道或大量账户复用需求时,再评估抽象公共支付模型。 --- ## 第八轮:店铺进入收款设置后如何简单操作 ### 用户问题 > 我还是不够明白,通过店铺进入管理入口,应该是怎么操作的,操作起来应该简单明了 ### 分析过程 - 隐藏底层账户、渠道、绑定和技术凭证概念。 - 从门店经营者角度,只展示支付方式、收款归属和开通状态。 - 将操作流程收敛为店铺列表入口和收款设置页面。 ### 结论 当时形成的界面原则: > 从用户角度,支付属于店铺;底层如何保存和调用凭证由系统处理。 店铺列表入口: ```text 店铺列表 ┌──────────┬────────┬──────────┐ │ 店铺名称 │ 营业状态 │ 操作 │ ├──────────┼────────┼──────────┤ │ 王记一店 │ 营业中 │ 编辑|收款设置 │ │ 王记二店 │ 营业中 │ 编辑|收款设置 │ └──────────┴────────┴──────────┘ ``` 点击“收款设置”进入当前店铺的收款页面: ```text 王记一店 · 收款设置 当前状态:正常收款 收款商户:王记餐饮有限公司 已开通:OMG ``` 支付渠道卡片: ```text ┌─────────────────────────────┐ │ OMG │ │ 状态:已开通 │ │ 收款商户:王记餐饮有限公司 │ │ [编辑配置] [停用] │ └─────────────────────────────┘ ┌─────────────────────────────┐ │ LINE Pay │ │ 状态:未开通 │ │ [开通 LINE Pay] │ └─────────────────────────────┘ ``` 普通商户不需要看到 `payment_account_id`、`holder_type` 等内部字段。`MerchantID`、`StoreID`、`Channel ID` 等可以放入“技术信息”区域或仅允许平台运营人员编辑。 页面只需让用户明确三件事: 1. 当前店铺能使用哪些支付方式。 2. 款项直接结算给哪个商户。 3. 当前渠道是否已开通。 --- ## 第九轮:确认平台不参与收款后是否需要简化 ### 用户问题 > 这个会不会太复杂了,现在明确平台不管收款,支付直接进入商户 ### 讨论结论 确认此前包含平台商、子商户、平台结算主体和通用支付账户的设计对于当前范围过于复杂。 业务关系简化为: ```text 店铺 ↓ 配置支付渠道 OMG / LINE Pay ↓ 直接结算 商户银行账户 ``` Foodie 只负责技术处理,不接触结算资金: - 发起支付。 - 接收并验证回调。 - 更新订单支付状态。 - 提供交易查询和退款能力。 因此暂不实现: - `PlatformID`。 - 平台商/子商户模型。 - 平台代收和分账。 - 平台资金账户。 - 二次结算逻辑。 夜市摊位遵循同一原则:摊位绑定谁的支付商户账户,支付渠道就直接把钱结算给谁,Foodie 不参与后续款项再分配。 --- ## 第十轮:最终选择店铺级简单配置 ### 用户决定 > 我们还是简单一点直接在店铺设置就行了,多店铺的情况也不多 ### 最终结论 当前阶段不建立独立的 `payment_account`、`store_payment_binding` 或通用支付域模型,直接按店铺配置。 OMG 使用: ```text pos_store_omg - store_id - merchant_id - omg_store_id - hash_key - hash_iv - enabled_payments - is_enabled - status ``` 管理入口: ```text 店铺管理 → 收款设置 → OMG ``` 少量多店铺需要共用同一个 OMG 商户时: - 每个店铺分别保存相同的 `MerchantID / HashKey / HashIV`。 - 每个店铺使用不同的 OMG `StoreID`。 - 数据库不能继续强制 `merchant_id` 全局唯一。 - 回调必须通过 `MerchantTradeNo` 找到订单和店铺,不能只使用 `MerchantID` 反查唯一店铺。 后续接入 LINE Pay 时,再增加店铺级 `pos_store_linepay` 配置;不为尚未发生的大规模支付账户复用需求提前增加抽象。 ## 实施时必须保留的安全要求 即使采用简单方案,以下要求仍不能省略: 1. `HashKey / HashIV / Channel Secret` 加密保存,前端和普通管理接口只显示掩码。 2. 支付回调必须校验签名、金额、订单状态和渠道商户编号。 3. 支付流水记录当次使用的 `store_id`、`MerchantID`、`StoreID` 和渠道交易编号。 4. 回调以 `MerchantTradeNo` 或平台唯一支付流水定位订单,不能依赖 `MerchantID` 与店铺一对一。 5. 重复回调必须幂等处理,不能重复更新订单或触发重复业务动作。 ## 后续重新评估架构的触发条件 只有出现以下任一情况时,再考虑抽象支付账户和店铺绑定模型: - 大量商户需要多个店铺共用同一套支付凭证。 - 同一店铺需要同时管理多个独立结算账户。 - 接入三个以上直连支付渠道,店铺级表出现大量重复逻辑。 - Foodie 的业务模式改变,需要平台商、子商户、代收或分账。 - 凭证管理需要统一迁移到外部密钥管理服务。