Files
junhong_cmp_fiber/pkg/wechat/config.go
huang 5b283285a2
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m11s
fix: 修复微信支付容器内 mkdir 权限错误,新增 wechat_v2 支付渠道
- NewPaymentAppFromConfig 加 Log.Stdout=true,禁止 PowerWeChat SDK 在容器
  工作目录创建 wechat/ 日志文件夹(Permission denied 根因)
- 新增 ProviderTypeWechatV2 = wechat_v2 常量,与 wechat(v3) 独立区分
- 新增 PaymentV2Service:直连统一下单 v2 接口,XML + MD5 签名
- newPaymentProvider 按 ProviderType 分支路由到对应版本
2026-03-31 12:12:13 +08:00

156 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package wechat
import (
"fmt"
"os"
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
"github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
// NewRedisCache 使用项目现有的 Redis 客户端创建 PowerWeChat 的 Redis Cache
func NewRedisCache(rdb *redis.Client) kernel.CacheInterface {
return kernel.NewRedisClient(&kernel.UniversalOptions{
Addrs: []string{rdb.Options().Addr},
Password: rdb.Options().Password,
DB: rdb.Options().DB,
})
}
// NewOfficialAccountAppFromConfig 从数据库配置创建微信公众号应用实例
func NewOfficialAccountAppFromConfig(wechatConfig *model.WechatConfig, cache kernel.CacheInterface, logger *zap.Logger) (*officialAccount.OfficialAccount, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if wechatConfig.OaAppID == "" || wechatConfig.OaAppSecret == "" {
return nil, fmt.Errorf("微信公众号配置不完整:缺少 oa_app_id 或 oa_app_secret")
}
userConfig := &officialAccount.UserConfig{
AppID: wechatConfig.OaAppID,
Secret: wechatConfig.OaAppSecret,
Cache: cache,
}
if wechatConfig.OaToken != "" {
userConfig.Token = wechatConfig.OaToken
}
if wechatConfig.OaAesKey != "" {
userConfig.AESKey = wechatConfig.OaAesKey
}
app, err := officialAccount.NewOfficialAccount(userConfig)
if err != nil {
logger.Error("创建微信公众号应用失败", zap.Error(err))
return nil, fmt.Errorf("创建微信公众号应用失败: %w", err)
}
logger.Info("微信公众号应用初始化成功", zap.String("app_id", wechatConfig.OaAppID))
return app, nil
}
// NewMiniAppServiceFromConfig 从数据库配置创建小程序服务实例
func NewMiniAppServiceFromConfig(wechatConfig *model.WechatConfig, logger *zap.Logger) (*MiniAppService, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if wechatConfig.MiniappAppID == "" || wechatConfig.MiniappAppSecret == "" {
return nil, fmt.Errorf("小程序配置不完整:缺少 miniapp_app_id 或 miniapp_app_secret")
}
return NewMiniAppService(wechatConfig.MiniappAppID, wechatConfig.MiniappAppSecret, logger), nil
}
// NewPaymentAppFromConfig 从数据库配置创建微信支付应用实例
func NewPaymentAppFromConfig(wechatConfig *model.WechatConfig, appID string, cache kernel.CacheInterface, logger *zap.Logger) (*payment.Payment, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if appID == "" {
return nil, fmt.Errorf("appID 不能为空")
}
if wechatConfig.WxMchID == "" || wechatConfig.WxAPIV3Key == "" || wechatConfig.WxSerialNo == "" {
return nil, fmt.Errorf("微信支付配置不完整:缺少 wx_mch_id/wx_api_v3_key/wx_serial_no")
}
certPath, err := writeWechatPemTempFile("wechat_cert_*.pem", wechatConfig.WxCertContent)
if err != nil {
return nil, fmt.Errorf("写入微信支付证书失败: %w", err)
}
keyPath, err := writeWechatPemTempFile("wechat_key_*.pem", wechatConfig.WxKeyContent)
if err != nil {
return nil, fmt.Errorf("写入微信支付私钥失败: %w", err)
}
userConfig := &payment.UserConfig{
AppID: appID,
MchID: wechatConfig.WxMchID,
MchApiV3Key: wechatConfig.WxAPIV3Key,
Key: wechatConfig.WxAPIV2Key,
CertPath: certPath,
KeyPath: keyPath,
SerialNo: wechatConfig.WxSerialNo,
Cache: cache,
NotifyURL: wechatConfig.WxNotifyURL,
// 禁止 PowerWeChat SDK 在当前目录创建 wechat/ 日志文件夹
// 容器内工作目录通常无写权限,会导致 "mkdir wechat: permission denied"
Log: payment.Log{
Stdout: true,
},
}
app, err := payment.NewPayment(userConfig)
if err != nil {
logger.Error("创建微信支付应用失败", zap.Error(err))
return nil, fmt.Errorf("创建微信支付应用失败: %w", err)
}
logger.Info("微信支付应用初始化成功",
zap.String("app_id", appID),
zap.String("mch_id", wechatConfig.WxMchID),
)
return app, nil
}
// NewPaymentV2ServiceFromConfig 从数据库配置创建微信支付 v2 服务实例
// v2 仅需 APIv2Key不要求证书序列号和 v3 密钥
func NewPaymentV2ServiceFromConfig(wechatConfig *model.WechatConfig, appID string, logger *zap.Logger) (*PaymentV2Service, error) {
if wechatConfig == nil {
return nil, fmt.Errorf("微信配置不能为空")
}
if appID == "" {
return nil, fmt.Errorf("appID 不能为空")
}
if wechatConfig.WxMchID == "" || wechatConfig.WxAPIV2Key == "" {
return nil, fmt.Errorf("微信支付 v2 配置不完整:缺少 wx_mch_id 或 wx_api_v2_key")
}
return NewPaymentV2Service(appID, wechatConfig.WxMchID, wechatConfig.WxAPIV2Key, wechatConfig.WxNotifyURL, logger), nil
}
func writeWechatPemTempFile(pattern, content string) (string, error) {
if content == "" {
return "", fmt.Errorf("证书内容不能为空")
}
file, err := os.CreateTemp("", pattern)
if err != nil {
return "", err
}
if _, err = file.WriteString(content); err != nil {
file.Close()
return "", err
}
if err = file.Close(); err != nil {
return "", err
}
return file.Name(), nil
}