api.md 11 KB

API Contracts: 可配置支付方式(031-pay-method-config)

Date: 2026-09-11 | Spec: spec.md | Data Model: data-model.md

所有接口遵守项目 Controller 规范:token 用 @RequestHeader String token,POST 业务参数用 @RequestBody DTO,GET 查询参数逐个 @RequestParam;DTO 字段不加 Bean Validation 注解,校验在 Service,错误信息走 MessageUtils.message。

0. 核心内部契约:PaymentMethodGateService(ruoyi-system)

单一校验入口,唯一闸门实现(spec FR-004)。

/** 校验支付方式可用,不可用抛 ServiceException(国际化);三下单入口统一下单时调用。 */
void assertUsable(String payType, GateScope scope, Long payeeUserId)

/** 列出某维度对收款方可用的支付方式(结算页可选项、骑手列表过滤共用)。 */
List<PayMethodOption> listAvailable(GateScope scope, Long payeeUserId)

enum GateScope { MERCHANT, RIDER_FLASH }

调用点(在用仅此三处;/addorder 一处随入口废弃留存,清理死代码时须一并移除并更新本表;禁止新增重复实现):

调用方 位置 参数
用户餐饮下单 UserOrderController.createOrder(setPayType 前后) payType=input.paymentMethod, MERCHANT, 商家主账号 userId(US7 起经 assertUsableForOrder,见 0.1)
商家建单 旧代客外送下单(已废弃 2026-09-29) PosOrderController /addorder(方法已标 @Deprecated) payType 固定 "1"(COD), MERCHANT —— 入口被 /system/orderShOprate/createOrder 取代,调用点仅随废弃代码留存,勿作为新改动基准
闪送下单 FlashDeliveryApplicationService.create(normalizePayType 处) payType, RIDER_FLASH, 无收款方(抢单前,只校验平台开关)
027 bankInfo ChantingStoreController.bankInfo OFFLINE_TRANSFER, MERCHANT, 商家 userId

错误 key(6 语言文件全配):pay.method.not.available(该支付方式当前不可用,请更换支付方式)。

0.1 现金(CASH,payType=4)闸门增量(2026-09-20 变更,spec US7/FR-006)

现金不设平台开关行、无就绪度,闸门判定需要订单类型上下文:

/** 带订单类型的校验重载:用户餐饮下单调用;其余入口沿用原签名(商家建单/闪送现金按现状放行)。 */
void assertUsableForOrder(String payType, GateScope scope, Long storeId, Long payeeUserId, Long orderType)
  • payType=4:orderType∈{1自取,2堂食} → 仅查商家勾选(info_user.pay_methods 含 CASH,或 NULL=全部接受)→ 通过;orderType=0外送 → 拒绝(pay.method.not.available);orderType=null(商家建单/闪送)→ 放行(现状)。
  • 商家勾选保存(§2.2)值域校验 groupsOf(MERCHANT) 扩展含 CASH;allGroups()(平台矩阵)不含 CASH。
  • 结算页 §7 listAvailable 增加可选 orderType 入参:type∈{1,2} 且商家接受时追加 {methodCode:"CASH", payType:"4", ready:true};orderType 缺省不追加(老客户端零变化)。

1. 平台端(foodie-admin-vue,权限 pay:method:config,操作记 @Log)

1.1 查询开关矩阵

GET /system/payMethodConfig/list

返回:方式×维度全矩阵(含缺行按"开"补齐的虚拟行),每项 {methodCode, methodName(i18n key), scope, enabled, sort}。

1.2 保存开关

PUT /system/payMethodConfig
Body: { items: [ { methodCode, scope, enabled, remark? } ] }

批量 upsert;@Log(title="支付方式设置");@PreAuthorize("@ss.hasPermi('pay:method:config')")。

2026-10-09 备注:平台 GET 每行增加 remark,仅 MERCHANT 可提交非空文本,最多 500 字,支持换行。字段省略/null 保留已有值;空白文本保存为空字符串、清空说明。长度超限或骑手维度非空备注用 pay.method.config.invalid 拒绝整个批次;不改变开关语义。

2. 商家端 PC(foodie-store,token = 商家主账号/子账号)

2.1 支付方式设置页数据

GET /system/merchantPayMethods

返回:{ available: [ {methodCode, payType, ready} ], selected: ["COD", ...] }——available = 平台商家维度开放集合(附就绪度标记)追加 CASH 项(2026-09-20 变更:无平台开关、ready 恒 true,是否可用由商家勾选决定),selected = 商家当前选择(NULL 视为全部,返回空数组,由前端展示为全选)。就绪度口径(2026-09-29 变更):普通商家调用 listAvailableForMerchantSettings(ownerUserId, 门店IDs)——线上渠道按商家名下门店聚合(任一门店有启用凭证即 ready=true)、线下转账看主账号启用卡、COD/现金恒 true;前端对 ready=false 项渲染「未就绪」标签。

2026-10-09:商家设置 GET 在原 available/selected 外追加 remarks: { "LINE_PAY": "手續費依合約;消費後第三天入帳" }。按组代码返回平台商家维度的非空说明,缺配置返回 {},前端只展示 available 中对应方式。普通商家/摊位主共用此说明;PUT 不接收或写入备注。用户结算、骑手设置及闪送共用选项结构保持不变。备注是管理员输入原文,示例不代表平台真实费率或到账承诺。

2.2 保存选择

PUT /system/merchantPayMethods
Body: { methodCodes: ["COD", "CARD_OMG"] }   // 空数组=清空=回落全部;值域含 "CASH"(2026-09-20 变更)

写 info_user.pay_methods(主账号行;子账号可编辑,写其主账号——与 022 子账号权限体系一致)。

2.3 银行卡管理(商家/骑手通用)

GET    /system/bankCard                    // 本人卡列表(含启用标记)
POST   /system/bankCard                    // 新增 {bankName, accountNo, accountName}
PUT    /system/bankCard                    // 修改 {id, bankName, accountNo, accountName}
DELETE /system/bankCard/{id}               // 删除(启用卡删除后无启用卡→线下转账不就绪)
PUT    /system/bankCard/activate/{id}      // 启用某卡(同事务停旧卡)

校验:bankName ∈ 字典 taiwan_bank_list;条数上限 10;只能操作本人卡。

3. 骑手端(骑手 App 接口,本期无前端)

GET /system/riderFlashPayMethods           // 返回同 2.1 结构(scope=RIDER_FLASH)
PUT /system/riderFlashPayMethods           // { methodCodes: [...] },空=回落全部

写入 info_user.flash_pay_methods;要求 token userType=2。

4. 闪送(FlashDelivery 现有接口的增量)

  • 报价/下单:FlashDeliveryQuoteRequest / FlashDeliveryCreateRequest 增加可选 payType;下单时经闸门校验(RIDER_FLASH,仅平台开关层);缺省=现金 4(现状不变)。
  • 用户端可见性:报价响应/首页返回闪送维度可选支付方式列表(listAvailable(RIDER_FLASH, null))。
  • 骑手抢单列表:现有查询追加过滤——订单 payType ∈ 骑手接受集合(flash_pay_methods NULL=全部开放项)。pickupDistanceMeters 等既有行为不变。
  • 骑手确认收款(新):

    POST /system/flashDelivery/rider/orders/{id}/confirmPayment
    

条件:本人中单 + 状态已达已送达或之后;效果:payment_status 0→1 + 日志(RIDER);幂等:已确认再调返回成功。

5. 027 兼容改造

GET /chanting/store/bankInfo?id={storeId} —— 路径、匿名、返回结构(accountName/bankName/accountNo 三字段或 data=null)完全不变:

  • 数据源:info_user 三字段 → info_bank_card 该商家 is_active=1 的卡
  • 可见性:原"data != null 才显示"→ 闸门(OFFLINE_TRANSFER 平台开 ∩ 商家接受 ∩ 存在启用卡)不满足时返回 data=null,老客户端自然隐藏
  • 老客户端提交线下转账下单(paymentMethod=6)被闸门拒绝 → 国际化提示(FR-004 兜底)

6. i18n

后端 messages*.properties ×6:pay.method.not.available、银行卡校验类 key(pay.bankcard.*)。前端:admin-vue 支付方式设置页、foodie-store 支付设置页全部 key 四语言(vi/zh/tw/en),命名驼峰有意义(项目规范)。

7. 用户端结算页可用支付方式(App 渲染依据)

GET /system/storePayMethods?storeId={storeId}&type={type}    // type 可选(2026-09-20 变更):1自取/2堂食
  • 鉴权:匿名(@Anonymous,同 bankInfo 风格);App 结算页进入时调用,按返回渲染支付方式列表。
  • 逻辑:pos_store.id → user_id(商家主账号)→ listAvailable(MERCHANT, 主账号, orderType)。
  • 返回:data: [ { methodCode, payType, ready } ]——methodCode 组代码 / payType 对应数值(CARD_OMG 展开 2 与 5 两项)/ ready 就绪度。线下转账 ready=false 时不渲染(替代 027 时代"bankInfo!=null 才显示"的判断,App 可统一按本接口渲染)。
  • 现金项(2026-09-20 变更):type=1 或 2 且商家接受现金(勾选含 CASH 或未设置)时,追加 {methodCode:"CASH", payType:"4", ready:true};type=0 外送 或不传 type 不返回现金(老客户端零变化)。
  • 与 bankInfo 关系:本接口管"选项显隐",bankInfo 仍管"转账信息内容"(结构不变,第 5 节)。
  • 现金下单语义:现金仅接受 type∈{1,2};订单创建后 payStatus 置 1(同到付线下收款语义,直接进入"进行中",不经"待付款");type=0 直传现金被闸门拒绝(pay.method.not.available)。

8. 夜市摊位支付设置增量(2026-09-30,US8)

  • GET/PUT /system/merchantPayMethods:有效 userType=4 摊位主通过服务端 info_user.store_id 定位唯一未删除摊位,读取/写入 pos_store.pay_methods;GET 的线上就绪度只看该摊位凭证,线下转账只看该摊位启用卡。userType=3 夜市主不可调用。
  • GET/POST/PUT/DELETE /system/bankCard 与 PUT /system/bankCard/activate/{id}:摊位主仅操作 info_bank_card.store_id=本摊位 的卡,入参不得指定归属;同摊位多个有效摊位主共享。普通商家和骑手仍按原 user_id 归属。
  • 结算、用户下单与 bankInfo:MERCHANT 维度若门店 is_stall=1,支付选择读 pos_store.pay_methods,线下转账就绪度和返回卡读 info_bank_card.store_id;普通门店保持旧路径。
  • 停用/删除摊位主、缺失关联或已删除/非摊位门店均拒绝设置操作;通用门店/摊位编辑 JSON 不接受 payMethods 或 isStall,editStall 仅允许夜市主编辑自己的摊位并保留数据库中的支付路由归属字段。
  • 摊位卡新增及启用接口在事务内先锁对应摊位行,再计数或切换启用卡;同摊位多个摊位主的并发请求按摊位串行,普通商家/骑手旧路径不变。