功能标识:023-payment-provider-routing
创建日期:2026-08-31
状态:设计讨论暂停;已记录确认项,尚未进入 plan、tasks 或 implement
关联规格:
specs/019-line-payspecs/020-omg-payment-rebuild背景:当前订单 payType=2、OMG 创建接口、OMG 支付尝试表及查询/退款入口均直接绑定 OMG;LINE Pay 则使用另一套 Controller、Service 和支付尝试表。后续可能因费率变化,将信用卡与 Apple Pay 从 OMG 整体切换到其他支付商,因此需要把用户支付方式、门店支付路由和实际支付商分离。
用户端在线支付只保留以下三种稳定支付方式:
PosOrder.payType |
统一支付方式 | 说明 |
|---|---|---|
2 |
CREDIT_CARD |
信用卡支付,不再表示 OMG |
3 |
LINE_PAY |
LINE Pay,支付商固定为 LINE Pay |
5 |
APPLE_PAY |
Apple Pay |
现有数据库中 pay_type=5 的历史订单已确认没有业务价值,因此可把 5 重新定义为 Apple Pay,不保留原“余额支付”语义。
CARD_WALLET,同时覆盖 CREDIT_CARD 与 APPLE_PAY。providerCode=LINE_PAY,不读取可切换的银行卡类路由。采用“通用支付编排层 + 支付商适配器”,保留支付商协议实现,不在 Controller 中不断增加支付商条件分支。
App
-> 通用在线支付接口
-> PaymentOrchestrationService
|-- StorePaymentRouteService
|-- PaymentAttemptService
`-- PaymentProviderRegistry
|-- OmgPaymentProviderAdapter
|-- LinePayProviderAdapter
`-- FuturePaymentProviderAdapter
每个支付商适配器负责本支付商的:
checkoutAction 的转换。支付商通过注册表按 providerCode 选择,Controller 不包含 if OMG / else 业务分支。
采用“通用主表 + 各支付商一对一明细表”,LINE Pay 在逻辑上进入通用支付账本,但不把 LINE Pay 专属字段物理合并进通用表。
pos_payment_attempt
|-- pos_order_omg_attempt
|-- pos_order_line_payment
`-- pos_order_<future_provider>_payment
pos_payment_attempt通用支付尝试主表至少负责保存:
payment_method:CREDIT_CARD/LINE_PAY/APPLE_PAY;provider_code 和支付商展示名称快照;checkout_action_type;通用主表负责统一路由、客户端查询、历史支付商定位和跨支付商防重复支付。
pos_order_omg_attempt 继续保存 OMG 交易号、凭证快照、CheckMacValue 相关事实、OMG 回调和查询字段。pos_order_line_payment 继续保存 line_order_id、transaction_id、LINE 凭证版本、Web/App 地址、Capture 金额、LINE 精确状态和对账租约。payment_attempt_id 与通用主表一对一关联。新增门店支付路由,概念键为:
store_id + route_group=CARD_WALLET
-> provider_code
-> provider_credential_id
-> route_version
-> enabled/status
路由切换必须使用事务和版本条件原子完成,并记录旧支付商、新支付商、凭证版本、操作人和操作时间。
App 只认识稳定支付方式和客户端动作类型,不根据支付商名称执行不同支付逻辑。
首批动作类型包括:
WEB_FORM_POST:在当前 WebView 向指定地址提交服务端返回的表单字段;WEB_REDIRECT:在当前支付 WebView 打开指定地址;NATIVE_SDK:为未来必须使用原生 SDK 的支付商预留,实际接入时仍需 App 增加相应 SDK 能力。后端同时返回:
providerCode:稳定机器码;providerName:展示名称;checkoutAction.type:App 支付流程的唯一分流依据。providerCode/providerName 可用于展示、日志和客服定位,但 App 不得据此决定支付流程。
测试阶段不兼容旧 App,客户端支付入口直接调整为供应商无关接口:
POST /pay/online/create
POST /pay/online/query
POST /pay/online/retry
POST /pay/online/refund
GET /pay/online/methods?storeId=...
由于订单 payType 已能唯一确定三种支付方式,创建接口只接收订单号:
{
"orderId": "业务订单号"
}
客户端不得提交支付商、金额、商户交易号、凭证、网关地址或支付商原始字段。后端从订单读取支付方式、归属、门店和金额,再确定固定 LINE Pay 或门店银行卡类路由。
创建成功响应统一包含:
{
"attemptId": "公开支付尝试编号",
"orderId": "业务订单号",
"paymentMethod": "CREDIT_CARD",
"providerCode": "OMG",
"providerName": "OMG",
"status": "READY",
"checkoutAction": {
"type": "WEB_FORM_POST",
"url": "支付地址",
"fields": {}
}
}
第三方回调的签名、请求格式和响应文本不同,不强制合并为一个外部协议入口。各支付商保留固定回调或浏览器返回路径,例如:
POST /pay/provider/omg/notify
POST /pay/provider/<future-provider>/notify
GET /pay/provider/line/confirm
GET /pay/provider/line/cancel
支付商入口只处理协议解析和验签,之后必须通过支付商明细定位 pos_payment_attempt,进入统一支付状态处理。App 不调用这些路径。
旧 /pay/omg/create|query|retry|refund 和 /pay/line/create|query 不作为新客户端兼容接口保留。当前均为测试数据,不要求回填旧支付尝试。
payType=2/3/5 中的一种。payType 是否为受支持的在线支付方式。payType=3 固定选择 LINE Pay;payType=2/5 读取门店当前 CARD_WALLET 路由。checkoutAction。支付商创建结果必须能区分:
READY:支付入口已明确创建,可返回客户端动作;REJECTED:支付商明确拒绝且确认没有产生交易,可结束本次尝试;UNKNOWN:超时、断线或响应无法验证,必须保留活动尝试并查询原支付商,禁止改走其他支付商。以下内容在暂停前已经提出,但用户尚未逐项确认,因此不得视为最终需求:
CREATING
PENDING
PAID
FAILED
SUPERSEDED
REFUNDING
REFUNDED
MANUAL_REVIEW
待继续讨论:通用状态与 OMG、LINE Pay 精确状态的映射,活动键释放条件,支付成功迟到、重复付款及人工处理边界。
PAID/REFUNDED 不可逆。待继续讨论:错误码清单、退款状态机、对账策略、人工处理入口、迟到成功后的处理方式。
plan.md、tasks.md 和实施计划;本规格只记录 2026-08-31 已完成的架构讨论。当前没有授权实施,不修改生产代码、不执行数据库迁移,也不把待确认草案作为最终验收标准。后续继续讨论时,应从第 6 节开始确认,然后再进入 spec-kit 的 plan、tasks 和 implement 阶段。