# 业务逻辑与数据模型 (Business Logic & Data Model) v4.0 > 定义支付系统的核心数据结构和业务流程 ## 📊 数据库表结构 ### 1. 订单表 (orders) 存储业务订单信息,与支付解耦。 ```sql CREATE TABLE `orders` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `sn` VARCHAR(32) NOT NULL COMMENT '订单号 (业务唯一)', `user_id` VARCHAR(64) NOT NULL COMMENT '用户ID', `title` VARCHAR(128) NOT NULL COMMENT '订单标题', `price_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '订单原价 (分)', `pay_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '应付金额 (分)', `currency` VARCHAR(8) NOT NULL DEFAULT 'CNY' COMMENT '货币类型', `status` VARCHAR(20) NOT NULL DEFAULT 'created' COMMENT '状态: created/paying/paid/closed/refunded', `product_id` VARCHAR(64) DEFAULT NULL COMMENT '商品ID', `product_type` VARCHAR(32) DEFAULT NULL COMMENT '商品类型', `extra_data` JSON DEFAULT NULL COMMENT '扩展数据', `paid_at` DATETIME DEFAULT NULL COMMENT '支付时间', `closed_at` DATETIME DEFAULT NULL COMMENT '关闭时间', `expired_at` DATETIME DEFAULT NULL COMMENT '过期时间', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_sn` (`sn`), KEY `idx_user_id` (`user_id`), KEY `idx_status` (`status`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表'; ``` ### 2. 交易流水表 (pay_trades) 记录每一次支付尝试,一个订单可能有多次交易。 ```sql CREATE TABLE `pay_trades` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `trade_sn` VARCHAR(32) NOT NULL COMMENT '交易流水号 (系统生成)', `order_sn` VARCHAR(32) NOT NULL COMMENT '关联订单号', `user_id` VARCHAR(64) NOT NULL COMMENT '用户ID', `title` VARCHAR(128) NOT NULL COMMENT '交易标题', `amount` BIGINT UNSIGNED NOT NULL COMMENT '交易金额 (分)', `cash_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '现金支付金额 (分)', `coin_amount` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '虚拟币抵扣金额', `currency` VARCHAR(8) NOT NULL DEFAULT 'CNY' COMMENT '货币类型', `platform` VARCHAR(32) NOT NULL COMMENT '支付平台: alipay/wechat/paypal/stripe/usdt/coin', `platform_type` VARCHAR(32) DEFAULT NULL COMMENT '平台子类型: web/wap/jsapi/native/h5/app', `platform_sn` VARCHAR(64) DEFAULT NULL COMMENT '平台交易号', `platform_created_params` JSON DEFAULT NULL COMMENT '发送给平台的参数', `platform_created_result` JSON DEFAULT NULL COMMENT '平台返回的结果', `status` VARCHAR(20) NOT NULL DEFAULT 'paying' COMMENT '状态: paying/paid/closed/refunded', `type` VARCHAR(20) NOT NULL DEFAULT 'purchase' COMMENT '类型: purchase(购买)/recharge(充值)', `pay_time` DATETIME DEFAULT NULL COMMENT '支付时间', `notify_data` JSON DEFAULT NULL COMMENT '回调原始数据', `seller_id` VARCHAR(64) DEFAULT NULL COMMENT '卖家ID (多商户场景)', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_trade_sn` (`trade_sn`), KEY `idx_order_sn` (`order_sn`), KEY `idx_platform_sn` (`platform_sn`), KEY `idx_user_id` (`user_id`), KEY `idx_status` (`status`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='交易流水表'; ``` ### 3. 资金流水表 (cashflows) 记录账户资金变动(可选,用于虚拟币/钱包场景)。 ```sql CREATE TABLE `cashflows` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `sn` VARCHAR(32) NOT NULL COMMENT '流水号', `user_id` VARCHAR(64) NOT NULL COMMENT '用户ID', `type` VARCHAR(20) NOT NULL COMMENT '类型: inflow(入账)/outflow(出账)', `action` VARCHAR(32) NOT NULL COMMENT '动作: recharge/purchase/refund/transfer', `amount` BIGINT NOT NULL COMMENT '金额 (分,正数入账负数出账)', `currency` VARCHAR(8) NOT NULL DEFAULT 'CNY', `balance_before` BIGINT NOT NULL DEFAULT 0 COMMENT '变动前余额', `balance_after` BIGINT NOT NULL DEFAULT 0 COMMENT '变动后余额', `trade_sn` VARCHAR(32) DEFAULT NULL COMMENT '关联交易流水号', `order_sn` VARCHAR(32) DEFAULT NULL COMMENT '关联订单号', `remark` VARCHAR(256) DEFAULT NULL COMMENT '备注', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_sn` (`sn`), KEY `idx_user_id` (`user_id`), KEY `idx_trade_sn` (`trade_sn`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资金流水表'; ``` ### 4. 退款记录表 (refunds) ```sql CREATE TABLE `refunds` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `refund_sn` VARCHAR(32) NOT NULL COMMENT '退款单号', `trade_sn` VARCHAR(32) NOT NULL COMMENT '原交易流水号', `order_sn` VARCHAR(32) NOT NULL COMMENT '原订单号', `amount` BIGINT UNSIGNED NOT NULL COMMENT '退款金额 (分)', `reason` VARCHAR(256) DEFAULT NULL COMMENT '退款原因', `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending/processing/success/failed', `platform_refund_sn` VARCHAR(64) DEFAULT NULL COMMENT '平台退款单号', `refunded_at` DATETIME DEFAULT NULL COMMENT '退款完成时间', `operator_id` VARCHAR(64) DEFAULT NULL COMMENT '操作人ID', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_refund_sn` (`refund_sn`), KEY `idx_trade_sn` (`trade_sn`), KEY `idx_order_sn` (`order_sn`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款记录表'; ``` --- ## 🔄 状态机定义 ### 订单状态 (Order Status) ``` ┌─────────────────────────────────────────────────┐ │ created │ │ │ │ │ ┌───────────┼───────────┐ │ │ ▼ │ ▼ │ │ paying ────────┼───────► closed │ │ │ │ │ │ ▼ │ │ │ paid ─────────┼───────► refunded │ │ │ │ └─────────────────────────────────────────────────┘ 状态说明: - created: 订单已创建,等待支付 - paying: 支付中 (已发起支付请求) - paid: 已支付 - closed: 已关闭 (超时/主动取消) - refunded: 已退款 ``` ### 交易状态 (Trade Status) ``` paying → paid ↓ ↓ closed refunded 状态说明: - paying: 支付中 - paid: 支付成功 - closed: 交易关闭 - refunded: 已退款 ``` --- ## 🔢 编号规则 ### 订单号 (order_sn) ``` 格式: YYYYMMDD + 6位随机数 示例: 202401170001 生成规则: 1. 日期前缀保证每日唯一空间 2. 随机数使用分布式ID生成器 3. 支持前缀自定义 (如区分业务线) ``` ### 交易流水号 (trade_sn) ``` 格式: T + YYYYMMDD + HHmmss + 5位随机数 示例: T20240117100530123456 生成规则: 1. 前缀 T 标识交易类型 2. 精确到秒的时间戳 3. 5位随机数防碰撞 ``` --- ## 📋 核心业务流程 ### 1. 标准支付流程 ```sequence 用户 -> 业务系统: 1. 提交订单 业务系统 -> 支付模块: 2. 创建订单 (create_order) 支付模块 -> 业务系统: 3. 返回 order_sn 用户 -> 支付模块: 4. 选择支付方式并支付 (checkout) 支付模块 -> 支付平台: 5. 创建平台交易 支付平台 -> 支付模块: 6. 返回支付参数 支付模块 -> 用户: 7. 返回支付数据 (二维码/跳转链接) 用户 -> 支付平台: 8. 完成支付 支付平台 -> 支付模块: 9. 异步回调 (notify) 支付模块 -> 支付模块: 10. 验签 + 更新状态 支付模块 -> 业务系统: 11. 触发业务回调 (发货/开通) ``` ### 2. 支付回调处理流程 ```python def handle_notify(gateway, data): # 1. 加载对应的支付网关驱动 driver = PaymentFactory.create(gateway) # 2. 验证签名 if not driver.verify_sign(data): raise SignatureError("签名验证失败") # 3. 解析回调数据 parsed = driver.parse_notify(data) trade_sn = parsed['trade_sn'] # 4. 幂等性检查 trade = Trade.get_by_sn(trade_sn) if trade.status == 'paid': return driver.success_response() # 已处理过,直接返回成功 # 5. 金额校验 if parsed['amount'] != trade.cash_amount: raise AmountMismatchError("金额不匹配") # 6. 更新交易状态 trade.update({ 'status': 'paid', 'platform_sn': parsed['platform_sn'], 'pay_time': parsed['pay_time'], 'notify_data': data }) # 7. 更新订单状态 order = Order.get_by_sn(trade.order_sn) order.update({'status': 'paid', 'paid_at': now()}) # 8. 触发业务回调 dispatch_event('order.paid', order) # 9. 返回成功响应 return driver.success_response() ``` ### 3. 退款流程 ```python def apply_refund(trade_sn, amount, reason): trade = Trade.get_by_sn(trade_sn) # 1. 状态检查 if trade.status != 'paid': raise InvalidStatusError("只有已支付的交易可以退款") # 2. 创建退款记录 refund = Refund.create({ 'refund_sn': generate_refund_sn(), 'trade_sn': trade_sn, 'amount': amount, 'reason': reason, 'status': 'pending' }) # 3. 调用平台退款接口 driver = PaymentFactory.create(trade.platform) result = driver.refund({ 'trade_sn': trade_sn, 'refund_sn': refund.refund_sn, 'amount': amount }) # 4. 更新状态 if result.success: refund.update({'status': 'success', 'refunded_at': now()}) trade.update({'status': 'refunded'}) else: refund.update({'status': 'failed'}) return refund ``` --- ## 🏭 工厂模式设计 ```python class PaymentFactory: """支付网关工厂""" _drivers = { 'alipay': AlipayGateway, 'wechat': WechatGateway, 'paypal': PayPalGateway, 'stripe': StripeGateway, 'usdt': USDTGateway, 'coin': CoinGateway, } @classmethod def create(cls, gateway: str) -> AbstractGateway: gateway_name = gateway.split('_')[0] # wechat_jsapi -> wechat if gateway_name not in cls._drivers: raise ValueError(f"不支持的支付网关: {gateway}") driver_class = cls._drivers[gateway_name] return driver_class(config=get_payment_config(gateway_name)) ``` ```python class AbstractGateway(ABC): """支付网关抽象基类""" @abstractmethod def create_trade(self, data: dict) -> dict: """创建交易""" pass @abstractmethod def verify_sign(self, data: dict) -> bool: """验证签名""" pass @abstractmethod def parse_notify(self, data: dict) -> dict: """解析回调数据""" pass @abstractmethod def refund(self, data: dict) -> RefundResult: """发起退款""" pass @abstractmethod def query_trade(self, trade_sn: str) -> dict: """查询交易""" pass @abstractmethod def close_trade(self, trade_sn: str) -> bool: """关闭交易""" pass def success_response(self) -> str: """回调成功响应""" return "success" ``` --- ## 💰 金额处理规范 ### 1. 存储规则 - 数据库统一使用**分**为单位 (BIGINT) - 避免浮点数精度问题 ### 2. 接口规则 - API 输入输出统一使用**元**为单位 - 内部转换: `分 = 元 × 100` ### 3. 转换示例 ```python # 元转分 (API输入 -> 数据库) def yuan_to_fen(yuan: float) -> int: return int(round(yuan * 100)) # 分转元 (数据库 -> API输出) def fen_to_yuan(fen: int) -> float: return round(fen / 100, 2) ``` --- ## 🔐 幂等性设计 ### 1. 订单创建幂等 - 使用 `(user_id, product_id, created_date)` 组合判断 - 或使用客户端传入的幂等键 `idempotency_key` ### 2. 支付回调幂等 - 检查交易状态,已支付则直接返回成功 - 使用数据库事务 + 行锁保证并发安全 ### 3. 退款幂等 - 同一笔交易只能退款一次 (或限制总退款金额)