海外版外卖系统架构:i18n 与支付意图解耦
欧美单业态初创若不会写代码评审海外版外卖系统时最容易把两件事焊死页面语言和扣款通道。改一句英语提示webhook 对不上加一个西语包原支付意图被重建。架构上应先切开 i18n 目录与支付意图而不是绑在同一套定制 if-else。下文为教学示意。痛点展示字符串闯进资金域不会开发时外包常把lang直接传进收单适配器。页面选 English代码就走另一家 PSP选 Español再 new 一笔 intent。财务看到的是「同一桌订单、两条资金单」客服看到的是「换语言等于重新付款」。根因不是文案翻译慢而是展示域与资金域没有模块边界。中等预算应先把这两包拆开再谈皮肤和运营文案。模块目录树换包不等于换通道overseas-wm-eu/ ├── apps/ │ ├── diner-web/ # 只消费 message-key │ └── shop-web/ ├── i18n-catalog/ │ ├── bundles/ │ │ ├── en-US.yaml │ │ └── es-ES.yaml │ ├── catalog-api/ │ └── missing-key-guard/ ├── payment-intent/ │ ├── intent-api/ │ ├── psp-adapter-card/ │ ├── psp-adapter-wallet/ │ └── webhook-ingress/ ├── takeout-order/ # 订单只存 intent_id ├── mid-shared/ │ ├── user-master/ │ └── admin-console/ └── ops/ ├── locale-volume/ └── psp-secrets/这棵树要验收的点很具体更新bundles/es-ES.yaml不得删除或改写已有payment_intent行。diner-web禁止拼接 PSP 商户号它只能带Accept-Language去catalog-api。技术栈列表示意运行时Java 17、Spring Boot 3catalog-api 与 intent-api 分进程资源格式YAML locale bundle启动时对键全集做差集校验缓存Redis 保存 bundle 的 SHA-256热更新不杀 JVM队列webhook 先入队再改 intent 状态避免网关超时重放打花账数据MySQL 8msg_bundle与pay_intent至少分 schema发布Compose语言文件只读挂载通道密钥单独 secret语言包配置缺键必须失败# ops/locale-volume/catalog.yaml示意catalog:default_locale:en-USfallback_policy:FAIL_CLOSEDbundles:-locale:en-USfile:/locale/en-US.yamlchecksum:sha256:9c1a88e0-locale:es-ESfile:/locale/es-ES.yamlchecksum:sha256:4ab21c77FAIL_CLOSED的意思是键不存在就拒绝渲染和下单而不是悄悄回中文。欧美试点如果静默回中文运营会以为「已本地化」实际用户看到的是错语言测试永远绿。checksum 用于滚动发布新文件哈希对不上catalog-api 保持旧 bundle避免半包上线。支付意图配置只认通道 ID# ops/psp-secrets/intent-channels.yaml示意payment_intent:capture_mode:MANUALcurrency:USDchannels:-id:card_usadapter:psp-adapter-cardwebhook_path:/hooks/card-id:wallet_euadapter:psp-adapter-walletwebhook_path:/hooks/wallet这份文件禁止出现任何展示字符串。intent 只认channel_id、金额、币种、捕获模式。MANUAL捕获把「授权」和「请款」分开税费或小费后补时不必因改文案而重开 intent。通道凭证与语言 YAML 分文件权限也可以分人运营改文案出纳管密钥。启动顺序语言卷未就绪则拒单# 示意欧美试点机不挂中文包set-euopipefaildockercompose up-dmysql redisinstall-d/data/localecpbundles/en-US.yaml /data/locale/dockercompose up-dcatalog-apicurl-fsSlocalhost:8081/catalog/health|grepbundle:en-USdockercompose up-dintent-api webhook-ingress takeout-ordercurl-fsSlocalhost:8082/intents/_ready这段解决启动竞态页面已经是英语intent 却按未加载通道创建。catalog-api健康检查不过后面的收单进程不准接受POST /orders。密钥目录未挂载时intent-api应直接退出而不是用空 adapter 顶着跑。订单域只保存 intent 指针takeout-order 表建议只留payment_intent_id不落 PSP 原始报文全文。intent 自身走独立状态REQUIRES_ACTION→AUTHORIZED→CAPTURED或VOIDED。语言切换只改 HTTP 头不改 intent 主键也不改capture_mode。若业务坚持「换语言等于换通道」必须新建 intent 并作废旧单禁止原地 UPDATE channel。webhook 幂等键用intent_id psp_event_id不要用按钮文案或翻译后的状态名。三联编号订单、意图、通道事件对账最少要能串三条稳定 IDorder_no、payment_intent_id、psp_event_id。页面上的「已付款 / Paid」只是翻译不能当关联键。catalog-api 返回的是message_key加当前 locale 的文本订单库不存译句。intent-api 创建意图时写入金额最小单位与币种与Accept-Language解耦。PSP 把 locale 当风险参数时也只能放在 adapter 的附加字段禁止回写channel_id。客服换语言排查时三条 ID 必须仍指向同一资金事实。catalog-api 契约键全集先于页面bundle 发布前应对 en-US 与 es-ES 做键差集。只在西语包补了一半键就上线FAIL_CLOSED 会把下单接口打成大面积失败。这比静默回中文更吵但能阻止「页面看起来本地化了」。运营改文案走 admin-console 的 key 编辑提交后只更新 catalog schema。编辑器不应出现 PSP 商户号输入框避免文案岗碰到资金凭证。热更新读 Redis 中的 bundle 哈希哈希变化才切换内存字典请求中途不换包。webhook 验签与语言包无关验签密钥只存在psp-secretsingress 校验通过后才投递队列。重放攻击靠psp_event_id去重不靠通知邮件里的英文句子。通道回调体即使带localeesintent 状态机也不读取该字段。若某家 PSP 强制要求店铺展示名应放在 adapter 的静态商户资料而不是 locale YAML。签名失败时记录通道错误码禁止把错误文案翻译后再写入 intent。翻译可以后补资金状态只能由验签后的事件推进。成品边界共享底座不共享 if-else光合同城海外版外卖按成品交付源码与库可落在客户指定环境。初创团队不会开发时仍可按目录验收谁改 YAML、谁保管 secret、缺键是否失败。中台侧的用户主数据与统一后台给后续扩城复用登录和权限模板。首期不必打开其它业态菜单叠加模块时复用同一套 catalog / intent 边界而不是再复制支付分支。验收清单同一intent_id在 en-US / es-ES 间切换后金额、币种、channel_id不变删除某个 message-key 后下单接口返回缺键错误而不是回退中文只滚动 locale volumewebhook 验签文件无需重发未启用的wallet_eu直调返回明确拒绝码订单可用intent_id反查资金状态不靠页面句子对账catalog-api 与 intent-api 分健康检查禁止捆绑成一个探活创建 intent 的请求头即使带Accept-Language落库字段仍不含 locale小费后补 capture 不新建intent_idRedis 中 bundle 哈希与文件 checksum 一致否则拒绝切包反模式把 PSP 的 locale 参数当真相部分通道创建付款时允许传locale只影响托管页文案。若业务把该参数理解成「换语言即换通道」intent 会被重复创建。正确映射是托管页 locale 来自 catalog 的当前语言intent 的channel_id来自用户选的支付方式。二者可以同时出现在一次请求里但写入两张表回包后只更新 intent 状态。欧美试点常见失败是西语托管页取消支付英语页又开新 intent订单却仍挂旧 ID。takeout-order 应记录当前有效intent_id作废的 intent 只留审计不再推进履约。不会开发的团队可以用这一条当验收取消支付后旧 intent 为VOIDED订单未支付语言再切也不复活旧单。卡片通道与钱包通道的 webhook 路径分开避免验签密钥串用。串用的后果是西语包上线那天验签失败被误判成「翻译键缺失」。值班应先看 ingress 的验签计数再看 catalog 的缺键计数两类告警分看板。排障顺序建议固定先确认 catalog 哈希再确认 intent 通道最后才看页面译文。把顺序反了翻译同学会被拉去查资金资金同学会去改正文案。欧美初创不会开发时把这三步写进值班单比再加外包更有效。技术小结海外版外卖系统的第一刀不是加翻译员而是把 i18n 目录和支付意图做成两个可独立发布的模块。语言包管键、校验与哈希intent 管金额、币种、捕获模式和 PSP 适配。启动顺序、缺键失败、分文件密钥这三项能被脚本核对才算把「不会开发」转成可交接的架构约束。授权与请款分步时客服改语言不得触发二次授权。小费后补只生成新的 capture 请求intent 主键保持不变。多实例 catalog-api 靠 Redis 哈希对齐内存字典避免 A 机已切西语、B 机仍英包。哈希尚未传播完成时网关应把写请求钉在旧哈希读请求允许短暂双版本。不要用数据库行当语言包真源再同步到文件交付物应是可签名的 YAML 卷。库只存「当前生效哈希」便于审计谁切的包而不是把整本翻译塞进大字段。

相关新闻