收银台设计图

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 收银台完整支付流程图 │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
一、系统架构概览
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 业务层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 商城模块 │ │ VIP模块 │ │ 订阅模块 │ │ 其他业务 │ │
│ │ ShopModule │ │ VIPModule │ │ SubsModule │ │ ... │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │ │
│ │ 实现 IBizOrderProvider 接口 │
│ ▼ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ AppPaymentCashierService (收银台服务) │ │
│ │ ┌────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ bizOrderProviders: Map<bizType, IBizOrderProvider> // 业务订单提供者注册表 │ │ │
│ │ │ ├── 'PURCHASE' => ShopOrderProvider │ │ │
│ │ │ ├── 'MEMBERSHIP' => VIPOrderProvider │ │ │
│ │ │ └── 'SUBSCRIPTION' => SubsOrderProvider │ │ │
│ │ └────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

│ 调用

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 支付层 │
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ PaymentChannelManagerService (渠道管理) │ │
│ │ ┌─────────────────────────────────┐ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 内部渠道 (Internal) │ │ 外部渠道 (External) │ │ │
│ │ │ ┌───────────────────────────┐ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
│ │ │ │ WalletChannelStrategy │ │ │ │ WECHAT_JSAPI │ │ WECHAT_NATIVE│ │ ALIPAY_APP │ ... │ │ │
│ │ │ │ (钱包渠道策略) │ │ │ │ 微信JSAPI │ │ 微信扫码 │ │ 支付宝APP │ │ │ │
│ │ │ │ │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │
│ │ │ │ ┌─────────────────────┐ │ │ │ │ │ │ │ │ │
│ │ │ │ │ WalletService │ │ │ │ └─────────────────┼─────────────────┘ │ │ │
│ │ │ │ │ - hold() │ │ │ │ │ │ │ │
│ │ │ │ │ - capture() │ │ │ │ ▼ │ │ │
│ │ │ │ │ - release() │ │ │ │ PaymentChannelService │ │ │
│ │ │ │ │ - deposit() │ │ │ │ (第三方网关服务) │ │ │
│ │ │ │ └─────────────────────┘ │ │ │ - createPayment() │ │ │
│ │ │ └───────────────────────────┘ │ │ - handleCallback() │ │ │
│ │ └─────────────────────────────────┘ │ - queryPayment() │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 网关层 │
│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ IGatewayStrategy (网关策略接口) │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ WechatJsapiStrategy│ │WechatNativeStrategy│ │ AlipayAppStrategy │ │ AlipayH5Strategy │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ - init(config) │ │ - init(config) │ │ - init(config) │ │ - init(config) │ │ │
│ │ │ - createPayment() │ │ - createPayment() │ │ - createPayment() │ │ - createPayment() │ │ │
│ │ │ - queryPayment() │ │ - queryPayment() │ │ - queryPayment() │ │ - queryPayment() │ │ │
│ │ │ - verifyCallback() │ │ - verifyCallback() │ │ - verifyCallback()│ │ - verifyCallback()│ │ │
│ │ │ - parseCallback() │ │ - parseCallback() │ │ - parseCallback() │ │ - parseCallback() │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ 第三方支付平台 │ │
│ │ 微信支付 API 支付宝 API 其他... │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
二、业务模块接入收银台流程
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 业务模块如何接入收银台 │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 步骤 1: 实现 IBizOrderProvider 接口 │
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ // 商城模块示例 │ │
│ │ @Provide() │ │
│ │ export class ShopOrderProvider implements IBizOrderProvider { │ │
│ │ │ │
│ │ getBizType(): string { return 'PURCHASE'; } // 业务类型标识 │ │
│ │ │ │
│ │ async getOrderInfo(bizOrderNo: string, tenantKey?: string): Promise<BizOrderInfo> { │ │
│ │ const shopOrder = await this.shopOrderEntity.findOne({ orderNo: bizOrderNo }); │ │
│ │ return { │ │
│ │ bizOrderNo: shopOrder.orderNo, │ │
│ │ bizType: 'PURCHASE', │ │
│ │ userUid: shopOrder.userUid, │ │
│ │ merchantUid: shopOrder.shopId, // 店铺ID (用于分账) │ │
│ │ amount: shopOrder.totalAmount, // 订单金额 (分) │ │
│ │ description: shopOrder.title, │ │
│ │ status: shopOrder.payStatus, // PENDING / PAID / CANCELLED │ │
│ │ expireAt: shopOrder.expireAt, │ │
│ │ metadata: { items: shopOrder.items }, // 透传到支付成功事件 │ │
│ │ }; │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 步骤 2: 模块启动时注册到收银台 │
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ @Init() │ │
│ │ async init() { │ │
│ │ this.cashierService.registerBizOrderProvider(this.shopOrderProvider); │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 步骤 3: 监听支付成功事件更新业务订单状态 │
│ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ @Event('cashier.paid') │ │
│ │ async onPaymentSuccess(data: { bizType, bizOrderNo, amount, paidAt, metadata }) { │ │
│ │ if (data.bizType !== 'PURCHASE') return; │ │
│ │ │ │
│ │ // 更新商城订单状态为已支付 │ │
│ │ await this.shopOrderEntity.update({ orderNo: data.bizOrderNo }, { │ │
│ │ payStatus: 'PAID', │ │
│ │ paidAt: data.paidAt, │ │
│ │ }); │ │
│ │ │ │
│ │ // 执行后续业务: 减库存、发货通知等 │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
三、收银台支付完整流程
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────────┐ ┌─────────────┐
│ 用户APP │ │ 业务模块 │ │ 收银台服务 │ │ 钱包服务 │ │ 第三方网关 │ │ 微信/支付宝 │
│ │ │ (商城) │ │ Cashier │ │ Wallet │ │ Channel │ │ │
└────┬─────┘ └────┬─────┘ └──────┬───────┘ └──────┬───────┘ └───────┬────────┘ └──────┬──────┘
│ │ │ │ │ │
│ ① 创建商城订单 │ │ │ │
│──────────────>│ │ │ │ │
│ │ │ │ │ │
│ 商城订单号 │ │ │ │ │
│<──────────────│ │ │ │ │
│ │ │ │ │ │
│ ② 打开收银台 (bizOrderNo) │ │ │ │
│───────────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ③ 获取业务订单信息 │ │ │
│ │ │ getBizOrderInfo() │ │ │
│ │<────────────────│ │ │ │
│ │ │ │ │ │
│ │ BizOrderInfo │ │ │ │
│ │────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ④ 获取可用渠道列表 │ │ │
│ │ │─────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ │ [WALLET, WECHAT_JSAPI, ALIPAY_APP...] │ │
│ │ │<─────────────────────────────────────│ │
│ │ │ │ │ │
│ 收银台页面 (订单信息+渠道列表) │ │ │ │
│<───────────────────────────────│ │ │ │
│ │ │ │ │ │
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
│ 选择支付方式 │
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
│ │ │ │ │ │


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
四、纯钱包支付流程 (channelCode = 'WALLET')
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

│ │ │ │ │ │
│ ⑤ 选择钱包支付 │ │ │ │
│ pay(bizOrderNo, 'WALLET') │ │ │ │
│───────────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ⑥ 创建支付订单 │ │ │
│ │ │ PaymentOrderEntity │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑦ 冻结钱包金额 │ │ │
│ │ │ walletService.hold() │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ WalletHoldEntity │ │ │
│ │ │<──────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑧ 扣除冻结金额 │ │ │
│ │ │ walletService.capture() │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ transactionNo │ │ │
│ │ │<──────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑨ 更新订单状态 SUCCESS │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑩ 发布支付成功事件 │ │ │
│ │ │ emit('cashier.paid') │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ ⑪ 接收事件更新订单│ │ │ │
│ │<────────────────│ │ │ │
│ │ │ │ │ │
│ 支付成功 │ │ │ │ │
│<───────────────────────────────│ │ │ │
│ │ │ │ │ │


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
五、第三方支付流程 (channelCode = 'WECHAT_JSAPI')
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

│ │ │ │ │ │
│ ⑤ 选择微信支付 │ │ │ │
│ pay(bizOrderNo, 'WECHAT_JSAPI', openId) │ │ │
│───────────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ⑥ 创建支付订单 │ │ │
│ │ │ PaymentOrderEntity │ │ │
│ │ │ status=CREATED │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑦ 解析渠道 parseChannelCode('WECHAT_JSAPI') │
│ │ │ => { platform: 'wechat', scene: 'jsapi' } │
│ │ │ │ │ │
│ │ │ ⑧ 调用第三方网关 │ │ │
│ │ │ channelService.createPayment() │ │
│ │ │────────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ │ │ │ ⑨ 获取策略实例 │
│ │ │ │ │ getStrategy() │
│ │ │ │ │───────────────────│
│ │ │ │ │ │
│ │ │ │ │ ⑩ 调用微信 API │
│ │ │ │ │ createPayment() │
│ │ │ │ │──────────────────>│
│ │ │ │ │ │
│ │ │ │ │ 微信预支付凭证 │
│ │ │ │ │ (prepay_id 等) │
│ │ │ │ │<──────────────────│
│ │ │ │ │ │
│ │ │ │ │ ⑪ 创建交易记录 │
│ │ │ │ │ TransactionEntity │
│ │ │ │ │ status=PROCESSING│
│ │ │ │ │───────────────────│
│ │ │ │ │ │
│ │ │ { transactionNo, payData } │ │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ │ │ ⑫ 更新订单状态 │ │ │
│ │ │ status=PAYING │ │ │
│ │ │ gatewayTxNo=xxx │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ payData (唤起微信支付的参数) │ │ │ │
│<───────────────────────────────│ │ │ │
│ │ │ │ │ │
│ ⑬ 唤起微信支付 │ │ │ │
│─────────────────────────────────────────────────────────────────────────────────────────────>│
│ │ │ │ │ │
│ 用户完成支付 │ │ │ │ │
│<─────────────────────────────────────────────────────────────────────────────────────────────│
│ │ │ │ │ │


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
六、第三方支付回调处理流程
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

│ │ │ │ │ │
│ │ │ │ │ ⑭ 微信发送回调 │
│ │ │ │ │<──────────────────│
│ │ │ │ │ │
│ │ │ ⑮ 接收回调 │ │ │
│ │ │ handleCallback() │ │ │
│ │ │<─────────────────────────────────────-│ │
│ │ │ │ │ │
│ │ │ │ │ ⑯ 验证签名 │
│ │ │ │ │ verifyCallback() │
│ │ │ │ │───────────────────│
│ │ │ │ │ │
│ │ │ │ │ ⑰ 解析回调数据 │
│ │ │ │ │ parseCallback() │
│ │ │ │ │───────────────────│
│ │ │ │ │ │
│ │ │ │ │ ⑱ 更新交易状态 │
│ │ │ │ │ status=SUCCESS │
│ │ │ │ │───────────────────│
│ │ │ │ │ │
│ │ │ { success, transactionNo, callbackData } │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ │ │ ⑲ 更新支付订单状态 │ │ │
│ │ │ status=SUCCESS │ │ │
│ │ │ paidAt=now() │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑳ 发布支付成功事件 │ │ │
│ │ │ emit('cashier.paid') │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ ㉑ 接收事件更新订单│ │ │ │
│ │<────────────────│ │ │ │
│ │ │ │ │ │


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
七、混合支付流程 (钱包 + 第三方)
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

│ │ │ │ │ │
│ 用户选择: 钱包抵扣 50元 + 微信支付 50元 │ │ │
│ pay(bizOrderNo, 'WECHAT_JSAPI', walletDeduct=50) │ │ │
│───────────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ① 创建支付订单 │ │ │
│ │ │ totalAmount=100 │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ② 先冻结钱包金额 │ │ │
│ │ │ walletService.hold(50) │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ holdId │ │ │
│ │ │<──────────────────│ │ │
│ │ │ │ │ │
│ │ │ ③ 记录钱包冻结ID │ │ │
│ │ │ walletHoldId=xxx │ │ │
│ │ │ walletAmount=50 │ │ │
│ │ │ payMethod=MIXED │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ④ 计算第三方支付金额 │ │ │
│ │ │ gatewayAmount = 100 - 50 = 50 │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑤ 调用第三方网关 (金额=50) │ │
│ │ │ createPayment(amount=50) │ │
│ │ │────────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ │ payData │ │ │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ payData (唤起微信支付 50元) │ │ │ │
│<───────────────────────────────│ │ │ │
│ │ │ │ │ │
│ 用户完成微信支付 50元 │ │ │ │
│ │ │ │ │ │
│ │ │ ⑥ 收到微信回调 (成功) │ │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ │ │ ⑦ 扣除钱包冻结金额 │ │ │
│ │ │ walletService.capture(holdId) │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ transactionNo │ │ │
│ │ │<──────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑧ 更新订单 SUCCESS │ │ │
│ │ │ walletTxNo=xxx │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑨ 发布事件 │ │ │
│ │<────────────────│ │ │ │
│ │ │ │ │ │

═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
八、混合支付失败处理
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

│ │ │ │ │ │
│ │ │ 收到微信回调 (失败/超时) │ │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ │ │ ① 释放钱包冻结 │ │ │
│ │ │ walletService.release(holdId) │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ 钱包恢复原有余额 │ │ │
│ │ │<──────────────────│ │ │
│ │ │ │ │ │
│ │ │ ② 更新订单 FAILED │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
九、钱包充值流程 (特殊业务)
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

│ │ │ │ │ │
│ 充值 100 元 │ │ │ │
│ recharge(amount=100, 'WECHAT_JSAPI') │ │ │
│───────────────────────────────>│ │ │ │
│ │ │ │ │ │
│ │ │ ① 创建充值订单 │ │ │
│ │ │ bizType='RECHARGE' │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ② 调用第三方网关 │ │ │
│ │ │ createPayment(100)│ │ │
│ │ │────────────────────────────────────────>│ │
│ │ │ │ │ │
│ │ │ payData │ │ │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ payData │ │ │ │ │
│<───────────────────────────────│ │ │ │
│ │ │ │ │ │
│ 用户完成微信支付 │ │ │ │
│ │ │ │ │ │
│ │ │ ③ 收到微信回调 (成功) │ │
│ │ │<────────────────────────────────────────│ │
│ │ │ │ │ │
│ │ │ ④ 检测 bizType='RECHARGE' │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑤ 给用户钱包充值 │ │ │
│ │ │ walletService.deposit(100) │ │
│ │ │──────────────────>│ │ │
│ │ │ │ │ │
│ │ │ transactionNo │ │ │
│ │ │<──────────────────│ │ │
│ │ │ │ │ │
│ │ │ ⑥ 更新订单 SUCCESS │ │ │
│ │ │───────────────────│ │ │
│ │ │ │ │ │


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
十、关键数据结构说明
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 数据表关系图 │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ 业务订单表 (商城) │ │ 支付订单表 │ │ 网关交易表 │ │
│ │ shop_order │ │ payment_order │ │ payment_transaction│ │
│ ├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤ │
│ │ orderNo (PK) │─────>│ bizOrderNo (FK) │ │ transactionNo (PK) │ │
│ │ userUid │ │ orderNo (PK) │─────>│ outTradeNo (FK) │ │
│ │ totalAmount │ │ userUid │ │ platform │ │
│ │ payStatus │ │ totalAmount │ │ scene │ │
│ │ title │ │ walletAmount │ │ amount │ │
│ │ ... │ │ gatewayAmount │ │ status │ │
│ └─────────────────────┘ │ walletHoldId ───────│──┐ │ thirdPartyId │ │
│ │ walletTransactionNo │ │ │ payData │ │
│ │ gatewayTransactionNo│──│──>│ ... │ │
│ │ status │ │ └─────────────────────┘ │
│ │ bizType │ │ │
│ ┌─────────────────────┐ │ ... │ │ ┌─────────────────────┐ │
│ │ 钱包冻结表 │ └─────────────────────┘ │ │ 钱包交易流水表 │ │
│ │ wallet_hold │ │ │ wallet_transaction │ │
│ ├─────────────────────┤ │ ├─────────────────────┤ │
│ │ id (PK) │<──────────────────────────────┘ │ transactionNo (PK) │ │
│ │ userUid │ │ userUid │ │
│ │ amount │ │ type │ │
│ │ status │ │ amount │ │
│ │ bizOrderNo │ │ balanceBefore │ │
│ │ ... │ │ balanceAfter │ │
│ └─────────────────────┘ │ holdId ─────────────│──> │
│ │ ... │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 订单号说明 │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 业务订单号 (bizOrderNo) ─────> 商城/VIP等业务模块生成,如: SHOP202312280001 │
│ │ │
│ ▼ │
│ 支付订单号 (orderNo) ─────> 收银台生成,如: PAY202312280001 │
│ │ │
│ ├───> 钱包交易号 (walletTransactionNo) ─> 钱包模块生成,如: WTX202312280001 │
│ │ │
│ └───> 网关交易号 (gatewayTransactionNo) ─> 网关服务生成,如: GW202312280001 │
│ │ │
│ └───> 第三方订单号 (thirdPartyId) ─> 微信/支付宝返回,如: 4200001234... │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
十一、内外部渠道差异抹平机制
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ IChannelStrategy 统一接口 │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ interface IChannelStrategy { │
│ getCode(): string; // 渠道编码: WALLET / WECHAT_JSAPI / ALIPAY_APP │
│ getName(): string; // 渠道名称: 钱包支付 / 微信支付 / 支付宝 │
│ getType(): ChannelType; // 渠道类型: INTERNAL / EXTERNAL │
│ │
│ pay(request: PayRequest): Promise<PayResult>; // 统一支付接口 │
│ refund(request: RefundRequest): Promise<RefundResult>;// 统一退款接口 │
│ getBalance?(userUid, currency, tenantKey): Promise<number>; // 查询余额 (仅内部渠道) │
│ } │
│ │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────┐ ┌────────────────────────────────────┐ │
│ │ WalletChannelStrategy │ │ ExternalChannelAdapter │ │
│ │ (内部渠道 - 钱包) │ │ (外部渠道 - 适配器) │ │
│ ├────────────────────────────────────┤ ├────────────────────────────────────┤ │
│ │ │ │ │ │
│ │ pay(request): │ │ pay(request): │ │
│ │ ┌─────────────────────────────┐ │ │ ┌─────────────────────────────┐ │ │
│ │ │ 1. walletService.hold() │ │ │ │ 1. gateway.createPayment() │ │ │
│ │ │ 2. walletService.capture() │ │ │ │ => 返回 payData │ │ │
│ │ │ 3. 返回 { success: true } │ │ │ │ 2. 返回 { │ │ │
│ │ │ │ │ │ │ success: true, │ │ │
│ │ │ needCallback: false ◄────────│─│────────│────│ needCallback: true ◄───│─│── 需要异步回调 │
│ │ │ (同步完成) │ │ │ │ payData: {...} │ │ │
│ │ └─────────────────────────────┘ │ │ │ } │ │ │
│ │ │ │ └─────────────────────────────┘ │ │
│ │ getBalance(userUid): │ │ │ │
│ │ => walletService.getBalance() │ │ getBalance(): null │ │
│ │ │ │ (外部渠道不支持) │ │
│ │ │ │ │ │
│ └────────────────────────────────────┘ └────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ PayResult 返回值差异: │
│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ │ 内部渠道 (钱包) │ 外部渠道 (微信/支付宝) │ │
│ │ ───────────────────────│────────────────────────────────│─────────────────────────────────────────────── │ │
│ │ success │ true │ true │ │
│ │ needCallback │ false (同步完成) │ true (需等待异步回调) │ │
│ │ payData │ null │ { prepay_id, sign, ... } (唤起支付参数) │ │
│ │ transactionNo │ 钱包交易号 │ 网关交易号 │ │
│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 收银台如何处理 needCallback 差异 │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ CashierService.pay(): │
│ │
│ if (channelCode === 'WALLET') { │
│ // 钱包支付: 同步完成 │
│ await payWithWallet(order, amount); │
│ // hold -> capture -> 更新状态为 SUCCESS -> 发布事件 │
│ return { orderNo, payData: null }; │
│ │
│ } else { │
│ // 外部渠道: 异步等待回调 │
│ const payData = await payWithGateway(order, amount, platform, scene); │
│ // 创建交易记录 -> 更新状态为 PAYING │
│ return { orderNo, payData }; // 返回 payData 给前端唤起支付 │
│ │
│ // 后续通过 handleCallback() 处理: │
│ // 1. 验证签名 │
│ // 2. 更新订单状态为 SUCCESS │
│ // 3. 发布事件 │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘


═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
十二、总结
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 核心设计原则: │
│ │
│ 1. 业务解耦: 业务模块通过 IBizOrderProvider 接口提供订单信息,无需关心支付细节 │
│ │
│ 2. 渠道统一: IChannelStrategy 接口抹平内部/外部渠道差异 │
│ - 内部渠道 (钱包): 同步完成,直接返回成功 │
│ - 外部渠道 (微信等): 异步处理,通过回调确认结果 │
│ │
│ 3. 混合支付: 钱包可与任意外部渠道组合 │
│ - 先冻结钱包金额 │
│ - 外部支付成功后扣除冻结 │
│ - 外部支付失败则释放冻结 │
│ │
│ 4. 事件驱动: 支付成功通过 'cashier.paid' 事件通知业务模块 │
│ │
│ 5. 订单隔离: 每个模块维护自己的订单表,支付模块只关心金额和状态 │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
开发

完整的 App 钱包 + 第三方支付系统架构方案

2025-12-28 15:30:07

开发

Better Auth

2026-1-2 2:05:09

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
有新私信 私信列表
搜索