Files
mpay_v2_webman/doc/payment_system_implementation.md
2026-03-10 13:47:28 +08:00

6.7 KiB
Raw Blame History

支付系统核心实现说明

概述

已实现支付系统核心功能,包括:

  • 插件化支付通道系统(支持一个插件多个支付方式)
  • OpenAPI统一支付网关
  • 通道管理与配置
  • 订单管理与状态机
  • 异步通知机制

数据库初始化

执行以下SQL脚本创建表结构

mysql -u用户名 -p 数据库名 < database/mvp_payment_tables.sql

核心架构

1. 插件系统

  • 插件接口app/common/contracts/PayPluginInterface.php
  • 抽象基类app/common/contracts/AbstractPayPlugin.php(提供环境检测、产品选择等通用功能)
  • 插件类示例app/common/payment/LakalaPayment.php(命名规范:XxxPayment
  • 插件解析:由 PayServicePayOrderServicePluginService 直接根据 ma_pay_plugin 注册表中配置的 plugin_code / class_name 解析并实例化插件(默认约定类名为 app\common\payment\{Code}Payment

插件特点

  • 一个插件可以支持多个支付方式(如拉卡拉插件支持 alipay/wechat/unionpay
  • 支付产品由插件内部定义,不需要数据库字典表
  • 插件根据用户环境PC/H5/微信内/支付宝客户端)自动选择已开通的产品
  • 通道配置中,用户只需勾选确认开启了哪些产品(产品编码由插件定义)
  • 有些支付平台不区分产品,插件会根据通道配置自行处理
  • 通道配置表单由插件动态生成

2. 数据模型

  • Merchant:商户
  • MerchantApp商户应用AppId/AppSecret
  • PayMethod支付方式alipay/wechat等
  • PayChannel:支付通道(绑定到"插件+支付方式",配置已开通的产品列表)
  • PayOrder:支付订单
  • NotifyTask:商户通知任务

注意:支付产品不由数据库管理,而是由插件通过 getSupportedProducts() 方法定义。通道配置中的 enabled_products 字段存储的是用户勾选的产品编码数组。

3. 服务层

  • PayOrderService:订单业务编排(统一下单、查询)
  • ChannelRouterService:通道路由选择
  • NotifyService:商户通知服务

4. API接口

OpenAPI对外支付网关

  • POST /api/pay/unifiedOrder:统一下单(需要签名认证)
  • GET /api/pay/query:查询订单(需要签名认证)
  • POST /api/notify/alipay:支付宝回调
  • POST /api/notify/wechat:微信回调

管理后台API

  • GET /adminapi/channel/plugins:获取所有可用插件
  • GET /adminapi/channel/plugin/config-schema获取插件配置表单Schema
  • GET /adminapi/channel/plugin/products:获取插件支持的支付产品
  • GET /adminapi/channel/list:通道列表
  • GET /adminapi/channel/detail:通道详情
  • POST /adminapi/channel/save:保存通道

使用流程

1. 创建商户和应用

INSERT INTO ma_merchant (merchant_no, merchant_name, funds_mode, status) 
VALUES ('M001', '测试商户', 'direct', 1);

INSERT INTO ma_merchant_app (merchant_id, app_id, app_secret, app_name, notify_url, status) 
VALUES (1, 'app001', 'secret_key_here', '测试应用', 'https://example.com/notify', 1);

2. 配置支付通道

配置流程

  1. 创建通道:选择支付方式、支付插件,配置通道基本信息(显示名称、分成比例、通道成本、通道模式、限额等)
  2. 配置插件参数:通道创建后,再配置该通道的插件参数信息(通过插件的配置表单动态生成)

通过管理后台或直接操作数据库:

INSERT INTO ma_pay_channel (
    merchant_id, app_id, channel_code, channel_name, 
    plugin_code, method_code, enabled_products, config_json,
    split_ratio, channel_cost, channel_mode,
    daily_limit, daily_count, min_amount, max_amount,
    status
) VALUES (
    1, 1, 'CH001', '拉卡拉-支付宝通道',
    'lakala', 'alipay', 
    '["alipay_h5", "alipay_life"]',
    '{"merchant_id": "lakala_merchant", "secret_key": "xxx", "api_url": "https://api.lakala.com"}',
    100.00, 0.00, 'wallet',
    0.00, 0, NULL, NULL,
    1
);

通道字段说明

  • split_ratio: 分成比例(%默认100.00
  • channel_cost: 通道成本(%默认0.00
  • channel_mode: 通道模式,wallet-支付金额扣除手续费后加入商户余额,direct-直连到商户
  • daily_limit: 单日限额0表示不限制
  • daily_count: 单日限笔0表示不限制
  • min_amount: 单笔最小金额NULL表示不限制
  • max_amount: 单笔最大金额NULL表示不限制

3. 调用统一下单接口

curl -X POST http://localhost:8787/api/pay/unifiedOrder \
  -H "X-App-Id: app001" \
  -H "X-Timestamp: 1234567890" \
  -H "X-Nonce: abc123" \
  -H "X-Signature: calculated_signature" \
  -d '{
    "mch_order_no": "ORDER001",
    "pay_method": "alipay",
    "amount": 100.00,
    "subject": "测试订单",
    "body": "测试订单描述"
  }'

4. 签名算法

signString = "app_id={app_id}&timestamp={timestamp}&nonce={nonce}&method={method}&path={path}&body_sha256={body_sha256}"
signature = HMAC-SHA256(signString, app_secret)

扩展新插件

  1. 创建插件类,继承 AbstractPayPlugin,并按照 XxxPayment 命名放在 app/common/payment 目录:
namespace app\common\payment;

use app\common\contracts\AbstractPayPlugin;

class AlipayPayment extends AbstractPayPlugin
{
    public static function getCode(): string { return 'alipay'; }
    public static function getName(): string { return '支付宝直连'; }
    public static function getSupportedMethods(): array { return ['alipay']; }
    // ... 实现其他方法
}
  1. ma_pay_plugin 表中注册插件信息(也可通过后台管理界面维护):
INSERT INTO ma_pay_plugin (plugin_code, plugin_name, class_name, status)
VALUES ('alipay', '支付宝直连', 'app\\common\\payment\\AlipayPayment', 1);

注意事项

  1. 支付产品定义:支付产品由插件内部通过 getSupportedProducts() 方法定义,不需要数据库字典表。通道配置时,用户只需勾选已开通的产品编码。
  2. 环境检测:插件基类提供 detectEnvironment() 方法可根据UA判断环境
  3. 产品选择:插件根据环境从通道已开通产品中自动选择。如果通道配置为空或不区分产品,插件会根据配置自行处理。
  4. 通知重试:使用 NotifyMerchantJob 异步重试通知,支持指数退避
  5. 幂等性:统一下单接口支持幂等,相同 mch_order_no 返回已有订单

后续扩展

  • 账务系统(账户、分录、余额)
  • 结算系统(可结算金额、结算批次、打款)
  • 对账系统(渠道账单导入、差异处理)
  • 风控系统(规则引擎、风险预警)