维护说明:本文档已并入本站直接维护(单仓重构后原 backend/docs/*.md、前端 docs/*.md 不再随仓库发布)。
实现细节以 pay-unify 源码 为准,页面与源码的对应关系见相关资源。
支付证书管理使用指南
概述
本项目参考 1Panel 的证书管理方式,实现了友好的支付证书管理功能。支持通过Web界面上传、管理和应用支付证书,无需手动配置文件路径。
功能特点
-
多种上传方式
- 粘贴证书内容(适合复制粘贴证书文本)
- 选择本地文件路径(适合证书已在服务器上)
- 上传证书文件(适合从本地上传)
-
证书验证
- 自动验证证书格式(PEM格式)
- 解析证书有效期、颁发者等信息
- 检测证书类型(RSA、EC等)
-
证书管理
- 证书列表查看(分页、筛选)
- 设置默认证书
- 证书下载
- 证书删除(软删除)
-
自动应用
- 支付服务自动从数据库加载证书
- 支持证书热更新(无需重启服务)
- 自动保存到临时目录供SDK使用
数据模型
证书表结构 (tb_payment_cert)
1type PaymentCert struct {
2 ID uint // 证书ID
3 Name string // 证书名称
4 Description string // 证书描述
5 CertType string // 证书类型: alipay, wechat, paypal
6 FileType string // 文件类型: app_private_key, app_public_key, etc.
7 Content string // 证书内容(PEM格式)
8 FilePath string // 文件路径(可选)
9 Status string // 状态: active, expired, disabled
10 IsDefault bool // 是否为默认证书
11 ExpireDate *time.Time // 过期时间
12 Issuer string // 颁发者
13 SerialNumber string // 序列号
14 AppID string // 关联的应用ID
15 MchID string // 关联的商户ID
16 AppliedAt *time.Time // 应用时间
17 AppliedBy string // 应用人
18}
API 接口
以下接口前缀为 /api/v1(外部应用对应 /api/v2)。/api/v1/* 需管理员 JWT 会话,
/api/v2/* 需 OAuth2 Bearer + cert:manage scope。示例中的 Authorization: Bearer $TOKEN 请按实际凭证替换。
1. 上传证书(JSON方式)
接口: POST /api/v1/certs/upload
请求体:
1{
2 "name": "支付宝生产环境应用私钥",
3 "description": "2024年12月申请的应用私钥",
4 "certType": "alipay",
5 "fileType": "app_private_key",
6 "uploadType": "paste",
7 "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----",
8 "appId": "2021001234567890",
9 "isDefault": true
10}
参数说明:
certType: 证书类型,可选值: alipay(支付宝), wechat(微信), paypal
fileType: 文件类型
- 支付宝:
app_private_key, app_public_key, alipay_public_key, root_cert
- 微信:
private_key, serial_no
uploadType: 上传方式,可选值: paste(粘贴), local(本地路径), upload(文件上传)
响应:
1{
2 "message": "证书上传成功",
3 "data": {
4 "id": 1,
5 "name": "支付宝生产环境应用私钥",
6 "certType": "alipay",
7 "fileType": "app_private_key",
8 "status": "active",
9 "isDefault": true,
10 "createdAt": "2024-12-31T12:00:00Z"
11 }
12}
2. 上传证书文件
接口: POST /api/v1/certs/upload/file
Content-Type: multipart/form-data
表单字段:
name: 证书名称
description: 证书描述
certType: 证书类型
fileType: 文件类型
appId: 应用ID(可选)
mchId: 商户ID(可选)
isDefault: 是否默认(true/false)
file: 证书文件
cURL示例:
1curl -X POST http://localhost:8097/api/v1/certs/upload/file \
2 -H "Authorization: Bearer $TOKEN" \
3 -F "name=支付宝应用私钥" \
4 -F "certType=alipay" \
5 -F "fileType=app_private_key" \
6 -F "appId=2021001234567890" \
7 -F "isDefault=true" \
8 -F "file=@/path/to/privateKey.txt"
3. 获取证书列表
接口: POST /api/v1/certs/list
请求体:
1{
2 "certType": "alipay",
3 "page": 1,
4 "pageSize": 10
5}
响应:
1{
2 "data": {
3 "list": [
4 {
5 "id": 1,
6 "name": "支付宝应用私钥",
7 "certType": "alipay",
8 "fileType": "app_private_key",
9 "status": "active",
10 "isDefault": true,
11 "appId": "2021001234567890",
12 "createdAt": "2024-12-31T12:00:00Z"
13 }
14 ],
15 "total": 5,
16 "page": 1,
17 "pageSize": 10
18 }
19}
4. 获取证书详情
接口: GET /api/v1/certs/:id
响应:
1{
2 "data": {
3 "id": 1,
4 "name": "支付宝应用私钥",
5 "description": "生产环境使用",
6 "certType": "alipay",
7 "fileType": "app_private_key",
8 "status": "active",
9 "isDefault": true,
10 "appId": "2021001234567890",
11 "expireDate": "2025-12-31T23:59:59Z",
12 "createdAt": "2024-12-31T12:00:00Z"
13 }
14}
5. 设置默认证书
接口: POST /api/v1/certs/:id/default
响应:
1{
2 "message": "设置默认证书成功"
3}
6. 删除证书
接口: DELETE /api/v1/certs/:id
响应:
1{
2 "message": "证书删除成功"
3}
7. 下载证书
接口: GET /api/v1/certs/:id/download
响应: 证书文件下载
使用场景
场景1: 初次配置支付宝证书
- 上传应用私钥
1curl -X POST http://localhost:8097/api/v1/certs/upload \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "支付宝应用私钥",
6 "certType": "alipay",
7 "fileType": "app_private_key",
8 "uploadType": "paste",
9 "content": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
10 "appId": "2021001234567890",
11 "isDefault": true
12 }'
- 上传应用公钥证书
1curl -X POST http://localhost:8097/api/v1/certs/upload \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "支付宝应用公钥证书",
6 "certType": "alipay",
7 "fileType": "app_public_key",
8 "uploadType": "paste",
9 "content": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
10 "appId": "2021001234567890",
11 "isDefault": true
12 }'
- 上传支付宝公钥证书
1curl -X POST http://localhost:8097/api/v1/certs/upload \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "支付宝公钥证书",
6 "certType": "alipay",
7 "fileType": "alipay_public_key",
8 "uploadType": "paste",
9 "content": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
10 "appId": "2021001234567890",
11 "isDefault": true
12 }'
- 上传支付宝根证书
1curl -X POST http://localhost:8097/api/v1/certs/upload \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "支付宝根证书",
6 "certType": "alipay",
7 "fileType": "root_cert",
8 "uploadType": "paste",
9 "content": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
10 "appId": "2021001234567890",
11 "isDefault": true
12 }'
场景2: 配置微信支付证书
- 上传商户私钥
1curl -X POST http://localhost:8097/api/v1/certs/upload/file \
2 -H "Authorization: Bearer $TOKEN" \
3 -F "name=微信商户私钥" \
4 -F "certType=wechat" \
5 -F "fileType=private_key" \
6 -F "mchId=1234567890" \
7 -F "isDefault=true" \
8 -F "file=@apiclient_key.pem"
- 上传证书序列号
1curl -X POST http://localhost:8097/api/v1/certs/upload \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "微信证书序列号",
6 "certType": "wechat",
7 "fileType": "serial_no",
8 "uploadType": "paste",
9 "content": "1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C",
10 "mchId": "1234567890",
11 "isDefault": true
12 }'
场景3: 证书更新
- 查看当前证书列表
1curl -X POST http://localhost:8097/api/v1/certs/list \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{"certType": "alipay", "page": 1, "pageSize": 10}'
- 上传新证书(不设为默认)
1curl -X POST http://localhost:8097/api/v1/certs/upload \
2 -H "Authorization: Bearer $TOKEN" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "name": "支付宝新应用私钥",
6 "certType": "alipay",
7 "fileType": "app_private_key",
8 "uploadType": "paste",
9 "content": "...",
10 "appId": "2021001234567890",
11 "isDefault": false
12 }'
- 测试新证书无误后,设置为默认
1curl -X POST http://localhost:8097/api/v1/certs/123/default \
2 -H "Authorization: Bearer $TOKEN"
- 删除旧证书
1curl -X DELETE http://localhost:8097/api/v1/certs/1 \
2 -H "Authorization: Bearer $TOKEN"
代码集成
使用基于证书管理的支付服务
1import (
2 "github.com/difyz9/pay-unify/internal/pkg/service/payment"
3 "gorm.io/gorm"
4)
5
6// 创建支付宝服务
7func createAlipayService(db *gorm.DB) (*payment.CertBasedAlipayService, error) {
8 return payment.NewCertBasedAlipayService(
9 db,
10 "2021001234567890", // AppID
11 false, // sandbox=false表示生产环境
12 )
13}
14
15// 创建微信支付服务
16func createWechatService(db *gorm.DB) (*payment.CertBasedWechatService, error) {
17 return payment.NewCertBasedWechatService(
18 db,
19 "1234567890", // 商户号
20 "your-api-v3-key", // API V3密钥
21 )
22}
23
24// 使用证书辅助工具
25func useCertHelper(db *gorm.DB) {
26 helper := payment.NewCertHelper(db)
27
28 // 验证支付宝证书是否完整
29 if err := helper.ValidateAlipayCerts(); err != nil {
30 log.Printf("支付宝证书不完整: %v", err)
31 return
32 }
33
34 // 获取证书内容
35 privateKey, err := helper.GetAlipayPrivateKey()
36 if err != nil {
37 log.Printf("获取私钥失败: %v", err)
38 return
39 }
40
41 // 获取证书文件路径
42 certPath, err := helper.GetCertPath("alipay", "root_cert")
43 if err != nil {
44 log.Printf("获取证书路径失败: %v", err)
45 return
46 }
47}
安全建议
-
证书权限控制
- 建议为证书管理API添加管理员权限验证
- 数据库中的证书内容字段应加密存储
-
证书备份
-
证书有效期监控
-
默认证书保护
- 默认证书不允许直接删除
- 必须先设置其他证书为默认,才能删除原默认证书
数据库表自动创建
项目启动时会自动创建 tb_payment_cert 表,无需手动创建。
Swagger文档
启动服务后,访问 http://localhost:8097/swagger/index.html 查看完整的API文档。
注意事项
- 证书格式: 所有证书必须是PEM格式
- 私钥安全: 私钥内容存储在数据库中,请确保数据库安全
- 文件清理: 临时目录中的证书文件会在服务重启时重新生成
- 证书更新: 更新证书后,支付服务会自动重新加载(热更新)
与1Panel的差异
本实现参考了1Panel的证书管理设计,但针对支付场景做了以下调整:
- 证书类型: 支持支付宝、微信、PayPal等支付平台证书
- 文件类型: 针对每种支付方式定义了特定的证书文件类型
- 自动应用: 证书上传后自动应用到支付服务,无需手动配置
- 默认证书: 支持设置默认证书,简化配置
- AppID关联: 支持多个应用使用不同的证书
问题排查
1. 证书上传失败
检查证书格式是否为PEM格式:
1# 正确的PEM格式示例
2-----BEGIN RSA PRIVATE KEY-----
3MIIEpAIBAAKCAQEA...
4-----END RSA PRIVATE KEY-----
2. 支付服务初始化失败
确保已上传所有必需的证书:
- 支付宝: app_private_key, root_cert, alipay_public_key
- 微信: private_key, serial_no
3. 证书未生效
检查证书是否设置为默认:
1curl -X POST http://localhost:8097/api/v1/certs/:id/default \
2 -H "Authorization: Bearer $TOKEN"
后续优化