- 优化API参考文档的段落排版和表格对齐 - 补充签名机制和支付接口的详细说明- 完善错误码与解决方案的描述 - 统一文档中的代码引用和示例格式 docs(beego):优化Beego框架集成文档结构 - 改进Beego框架文档的换行和段落布局 - 完善控制器继承和中间件集成的说明 - 优化ORM模型注册和路由机制的描述- 统一文档中的技术术语表达方式 docs(docker): 改进Docker部署指南文档格式 - 优化Dockerfile多阶段构建的描述 - 完善docker-compose配置文件说明 - 改进本地部署步骤和故障排除指南- 统一文档中的命令行示例格式feat(supplier): 新增LianIns卡发送任务类型- 在枚举中添加SendCardTaskTypeEnumLianIns类型 - 更新GetAllSendCardTaskType函数返回值 - 实现LianIns任务类型的工厂方法 chore(deps): 更新项目依赖版本 - 升级github.com/bytedance/sonic至v1.14.2 - 升级github.com/duke-git/lancet/v2至v2.3.8 - 升级github.com/bytedance/sonic/loader至v0.4.0 - 移除natefinch/lumberjack和yaml.v2依赖- 清理间接依赖中的toml库引用
18 KiB
供应商集成架构
**Referenced Files in This Document** - [supplier_interface.go](file://internal/service/supplier/supplier_interface.go) - [aibo.go](file://internal/service/supplier/third_party/aibo.go) - [jd.go](file://internal/service/supplier/third_party/jd.go) - [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go) - [walmart.go](file://internal/service/supplier/third_party/walmart.go) - [init.go](file://internal/service/supplier/third_party/init.go)目录
接口定义与统一标准
供应商集成架构的核心是 PayInterface 接口,它为所有第三方支付渠道提供了统一的接入标准。该接口定义了处理支付流程所需的关键方法,确保了不同供应商的集成具有一致性和可预测性。
classDiagram
class PayInterface {
<<interface>>
+Scan(context.Context, order.OrderInfo, road.RoadInfo, merchant.MerchantInfo) ScanData
+PayNotify()
+PayQuery(order.OrderInfo, road.RoadInfo) bool
+PayQueryV2(order.OrderInfo, road.RoadInfo) supply_model.MsgModel
+PayFor(payfor.PayforInfo) string
+PayForNotify() string
+PayForQuery(payfor.PayforInfo) (string, string)
+BalanceQuery(road.RoadInfo) float64
+HasDependencyHTML() bool
}
class ScanData {
+Supplier string
+PayType string
+OrderNo string
+BankNo string
+OrderPrice string
+FactPrice string
+Status string
+PayUrl string
+Msg string
+ReturnData string
+UpStreamOrderNo string
}
Diagram sources
Section sources
接口方法说明
PayInterface 接口定义了以下核心方法:
- Scan: 处理扫码支付请求,接收订单、通道和商户信息,返回支付所需数据
- PayNotify: 处理第三方支付平台的异步回调通知
- PayQuery: 查询支付订单状态,返回布尔值表示是否成功
- PayQueryV2: 增强版支付查询,返回更详细的
MsgModel状态信息 - PayFor: 处理代付业务
- PayForNotify: 处理代付回调
- PayForQuery: 查询代付订单状态
- BalanceQuery: 查询供应商账户余额
- HasDependencyHTML: 判断是否需要依赖特定的支付页面
适配器实现与继承关系
各供应商通过实现 PayInterface 接口来接入系统,同时继承 web.Controller
以获得Web请求处理能力。这种设计模式遵循了适配器模式,将不同供应商的特定实现适配到统一的接口标准上。
classDiagram
class AiboCardImpl {
+HasDependencyHTML() bool
+generateSign(ctx context.Context, params map[string]any, key string) string
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string) (bool, string)
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
+PayNotify()
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
+PayFor(info payfor.PayforInfo) string
+PayForQuery(payFor payfor.PayforInfo) (string, string)
+BalanceQuery(roadInfo road.RoadInfo) float64
+PayForNotify() string
}
class JDCardImpl {
+HasDependencyHTML() bool
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string, merchantId string) (bool, string)
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
+PayNotify()
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
+PayFor(info payfor.PayforInfo) string
+PayForQuery(payFor payfor.PayforInfo) (string, string)
+BalanceQuery(roadInfo road.RoadInfo) float64
+PayForNotify() string
}
class TMAllGameImpl {
+HasDependencyHTML() bool
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.CardInfo, orderInfo order.OrderInfo) (bool, string)
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
+PayNotify()
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
+PayFor(info payfor.PayforInfo) string
+PayForQuery(payFor payfor.PayforInfo) (string, string)
+BalanceQuery(roadInfo road.RoadInfo) float64
+PayForNotify() string
}
class WalMartImpl {
+HasDependencyHTML() bool
+verifyCardNo(cardNo string) bool
+sendEncrypt(ci SendCardInfo, secret string) string
+notifyEncrypt(ci ResponseStruct, secret string) string
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string) (bool, string)
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
+PayNotify()
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
+PayFor(info payfor.PayforInfo) string
+PayForQuery(payFor payfor.PayforInfo) (string, string)
+BalanceQuery(roadInfo road.RoadInfo) float64
+PayForNotify() string
}
class web.Controller {
<<base class>>
}
AiboCardImpl --|> web.Controller
JDCardImpl --|> web.Controller
TMAllGameImpl --|> web.Controller
WalMartImpl --|> web.Controller
AiboCardImpl ..|> PayInterface
JDCardImpl ..|> PayInterface
TMAllGameImpl ..|> PayInterface
WalMartImpl ..|> PayInterface
Diagram sources
Section sources
继承与实现机制
所有供应商适配器都遵循相同的继承和实现模式:
- 继承
web.Controller: 通过嵌入web.Controller结构体,适配器获得了处理HTTP请求的能力,包括获取请求参数、设置响应等 - 实现
PayInterface: 适配器必须实现PayInterface接口的所有方法,确保了统一的调用接口 - 特定逻辑封装: 每个适配器包含供应商特定的辅助方法,如签名生成、数据加密等
这种设计实现了"组合优于继承"的原则,通过结构体嵌入(composition)而非传统继承来复用 web.Controller 的功能。
核心方法实现分析
Scan 方法实现
Scan 方法是处理支付请求的核心,各供应商的实现具有相似的处理流程,但包含特定于供应商的细节。
flowchart TD
Start([开始]) --> Unmarshal["反序列化 ExValue 数据"]
Unmarshal --> Validate{"数据有效?"}
Validate --> |否| ReturnError["返回错误: 订单有误"]
Validate --> |是| CallSendCard["调用 SendCard 发起支付"]
CallSendCard --> CheckResult{"支付成功?"}
CheckResult --> |否| ReturnFail["返回失败信息"]
CheckResult --> |是| BuildScanData["构建 ScanData 结果"]
BuildScanData --> SetStatus["设置状态为 '00'"]
SetStatus --> SetOrderInfo["设置订单号、金额等信息"]
SetOrderInfo --> SetReturnData["设置返回数据"]
SetReturnData --> ReturnSuccess["返回成功结果"]
ReturnError --> End([结束])
ReturnFail --> End
ReturnSuccess --> End
Diagram sources
Section sources
实现共性
所有 Scan 方法的实现都遵循以下通用流程:
- 上下文与追踪: 使用
otelTrace.Span创建分布式追踪上下文,便于监控和调试 - 数据解析: 从
orderInfo.ExValue中解析出卡密信息 - 支付发起: 调用供应商特定的
SendCard方法发起支付请求 - 结果处理: 根据支付结果构建并返回
ScanData对象
供应商特定差异
尽管流程相似,但各供应商的实现存在特定差异:
- 爱博 (Aibo): 使用MD5签名,通过
generateSign方法生成签名 - 京东卡 (JD): 需要传递商户ID作为附加参数
- 天猫游戏 (TMallGame): 根据
ProductUid区分确认收货和好评两种渠道 - 沃尔玛 (Walmart): 实现了卡号验证功能,防止特定字符的卡密
PayNotify 方法实现
PayNotify 方法处理第三方支付平台的异步回调,确保支付结果的准确性和安全性。
flowchart TD
Start([开始]) --> GetOrder["根据订单号获取订单信息"]
GetOrder --> ValidateOrder{"订单存在?"}
ValidateOrder --> |否| WriteFail["响应 FAIL"]
ValidateOrder --> |是| GetRoad["获取通道信息"]
GetRoad --> ValidateRoad{"通道有效?"}
ValidateRoad --> |否| WriteFail
ValidateRoad --> |是| VerifySign["验证签名"]
VerifySign --> ValidSign{"签名有效?"}
ValidSign --> |否| WriteFail
ValidSign --> |是| ProcessStatus["处理支付状态"]
ProcessStatus --> CheckStatus{"支付成功?"}
CheckStatus --> |是| SolveSuccess["调用 SolvePaySuccess"]
CheckStatus --> |否| SolveFail["调用 SolvePayFail"]
SolveSuccess --> WriteSuccess["响应 SUCCESS"]
SolveFail --> WriteSuccess
WriteFail --> End([结束])
WriteSuccess --> End
Diagram sources
Section sources
安全性保障
各供应商在 PayNotify 实现中都包含了严格的安全验证:
- 爱博: 使用
generateSign方法重新计算签名并与回调签名对比 - 天猫游戏: 使用
TmpEncrypt方法生成签名进行验证 - 沃尔玛: 使用专门的
notifyEncrypt方法生成签名进行验证 - 京东卡: 虽然没有显式签名验证,但通过订单号匹配确保安全性
PayQuery 方法实现
PayQuery 方法用于主动查询支付订单状态,实现了一致的查询逻辑。
flowchart TD
Start([开始]) --> GetCardData["从 CardReturnData 获取订单ID"]
GetCardData --> BuildParams["构建查询参数"]
BuildParams --> AddCommonParams["添加公共参数: app_key, timestamp"]
AddCommonParams --> GenerateSign["生成签名"]
GenerateSign --> CreateRequest["创建HTTP请求"]
CreateRequest --> SendRequest["发送请求到查询接口"]
SendRequest --> ParseResponse["解析响应"]
ParseResponse --> CheckCode{"code == 0?"}
CheckCode --> |否| ReturnFalse["返回 false"]
CheckCode --> |是| CheckStatus{"status == 9?"}
CheckStatus --> |是| ReturnTrue["返回 true"]
CheckStatus --> |否| ReturnFalse
ReturnTrue --> End([结束])
ReturnFalse --> End
Diagram sources
Section sources
查询逻辑一致性
所有供应商的 PayQuery 实现都表现出高度的一致性:
- 使用相同的查询URL (
GetMFCardQueryUrl) - 使用相同的参数结构 (
order_id,app_key,timestamp,sign) - 使用相同的签名生成方法 (
GetMD5SignMF) - 使用相同的成功判断标准 (
code == 0 && status == 9)
这种一致性大大简化了维护和扩展工作。
注册与依赖注入机制
系统通过注册表模式管理所有供应商适配器,实现了灵活的依赖注入和运行时查找。
classDiagram
class registerSupplier {
<<singleton>>
-registerSupplier map[string]supplier.PayInterface
+GetPaySupplierByCode(code string) supplier.PayInterface
}
class supplierCode2Name {
<<singleton>>
-supplierCode2Name map[string]string
}
class init {
+init()
}
init --> registerSupplier : "注册"
init --> supplierCode2Name : "初始化"
registerSupplier ..> PayInterface : "持有"
Diagram sources
Section sources
注册机制
供应商的注册在 init.go 文件的 init() 函数中完成,该函数在包初始化时自动执行:
func init() {
registerSupplier["JD"] = new(JDCardImpl)
registerSupplier["APPLE"] = new(AppleCardImpl)
registerSupplier["MF178"] = new(MFCardV2Impl)
// ... 其他供应商注册
}
这种设计确保了所有供应商在应用启动时就被正确注册,避免了运行时的初始化问题。
依赖注入方式
系统通过 GetPaySupplierByCode 函数提供依赖注入功能:
func GetPaySupplierByCode(code string) supplier.PayInterface {
return registerSupplier[code]
}
这种基于代码的查找机制具有以下优势:
- 松耦合: 调用方不需要知道具体的实现类型
- 可扩展: 添加新供应商只需在
init()函数中注册,无需修改调用代码 - 运行时灵活性: 可以根据配置动态选择供应商
接口驱动设计优势
基于 PayInterface 的供应商集成架构带来了显著的优势,特别是在可扩展性、可维护性和测试便利性方面。
可扩展性
接口驱动设计使得新增支付渠道变得简单而安全:
- 标准化接入: 所有新供应商必须实现相同的接口,确保了系统的一致性
- 最小化影响: 新增供应商不会影响现有代码,遵循开闭原则
- 快速集成: 开发者只需关注特定供应商的实现细节,无需修改核心逻辑
可维护性
接口抽象大大提高了代码的可维护性:
- 关注点分离: 每个适配器只负责特定供应商的逻辑,职责清晰
- 易于调试: 统一的接口使得问题定位更加容易
- 文档化契约: 接口本身作为契约,明确了各组件的交互方式
测试便利性
接口设计为单元测试和集成测试提供了便利:
- 可模拟性: 可以轻松创建接口的模拟实现进行测试
- 隔离测试: 可以独立测试每个适配器,无需依赖真实供应商
- 回归测试: 修改一个适配器不会影响其他适配器的测试
新增支付渠道指南
标准步骤
新增支付渠道需要遵循以下标准步骤:
- 创建适配器文件: 在
internal/service/supplier/third_party/目录下创建新的.go文件 - 定义结构体: 创建新的结构体并嵌入
web.Controller - 实现接口: 实现
PayInterface的所有方法 - 添加辅助方法: 实现供应商特定的签名、加密等辅助方法
- 注册适配器: 在
init.go的init()函数中注册新适配器 - 更新映射: 在
supplierCode2Name映射中添加供应商名称
代码示例
// 1. 定义结构体
type NewSupplierImpl struct {
web.Controller
}
// 2. 实现接口方法
func (c *NewSupplierImpl) Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData {
// 实现扫码支付逻辑
}
func (c *NewSupplierImpl) PayNotify() {
// 实现回调处理逻辑
}
func (c *NewSupplierImpl) PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool {
// 实现查询逻辑
}
// 3. 在 init() 函数中注册
func init() {
registerSupplier["NEW_SUPPLIER"] = new(NewSupplierImpl)
}
通过遵循这些步骤,可以确保新支付渠道的集成既快速又可靠,同时保持系统的整体一致性和稳定性。