增加租户套餐
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"server/models"
|
||||
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/auth"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/consts"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
|
||||
@@ -35,16 +37,26 @@ import (
|
||||
// appid 公众号 / 小程序 APPID
|
||||
// cert_serial_no 商户 API 证书序列号
|
||||
// api_v3_key APIv3 密钥
|
||||
// pub_key_id 微信支付公钥 ID(PUB_KEY_ID_ 开头;公钥模式必填,与 pub_key 成对)
|
||||
// pub_key 微信支付公钥 PEM 内容(公钥模式必填)
|
||||
//
|
||||
// 证书(cert_json):
|
||||
//
|
||||
// key_path apiclient_key.pem 上传后的服务端路径(不入库内容,见渠道证书上传接口)
|
||||
type WechatChannel struct{}
|
||||
|
||||
// connectTestOutTradeNo 测试连接用的探测单号:查询不存在的订单,返回 404 即视为鉴权链路通过
|
||||
const connectTestOutTradeNo = "CONNECT_TEST_NO_0000000000000001"
|
||||
|
||||
func (c *WechatChannel) Code() string { return ChannelWechat }
|
||||
func (c *WechatChannel) Name() string { return "微信支付" }
|
||||
|
||||
// buildClient 创建微信支付 APIv3 客户端(带自动证书更新与加解密能力)
|
||||
//
|
||||
// 初始化模式自动选择:
|
||||
// - 配置了「微信支付公钥」(pub_key_id + pub_key)时走公钥模式:
|
||||
// 2024 年后新开通商户默认使用公钥,平台证书下载接口对其停用(403 NOT_ENOUGH);
|
||||
// - 否则回退平台证书模式(自动下载并轮换平台证书,老商户适用)。
|
||||
func (c *WechatChannel) buildClient(cfg *ChannelConfig) (*core.Client, string, error) {
|
||||
mchID := cfg.Get("mch_id")
|
||||
serial := cfg.Get("cert_serial_no")
|
||||
@@ -66,6 +78,23 @@ func (c *WechatChannel) buildClient(cfg *ChannelConfig) (*core.Client, string, e
|
||||
return nil, "", fmt.Errorf("解析商户私钥失败: %w", err)
|
||||
}
|
||||
|
||||
pubKeyID := cfg.Get("pub_key_id")
|
||||
pubKeyPEM := cfg.Get("pub_key")
|
||||
if pubKeyID != "" && pubKeyPEM != "" {
|
||||
pubKey, perr := utils.LoadPublicKey(pubKeyPEM)
|
||||
if perr != nil {
|
||||
return nil, "", fmt.Errorf("解析微信支付公钥失败: %w", perr)
|
||||
}
|
||||
client, cerr := core.NewClient(context.Background(),
|
||||
option.WithMerchantCredential(mchID, serial, privateKey),
|
||||
option.WithWechatPayPublicKeyAuthCipher(mchID, serial, privateKey, pubKeyID, pubKey),
|
||||
)
|
||||
if cerr != nil {
|
||||
return nil, "", fmt.Errorf("创建微信支付客户端失败: %w", cerr)
|
||||
}
|
||||
return client, mchID, nil
|
||||
}
|
||||
|
||||
client, err := core.NewClient(context.Background(),
|
||||
option.WithMerchantCredential(mchID, serial, privateKey),
|
||||
option.WithWechatPayAutoAuthCipher(mchID, serial, privateKey, apiV3Key),
|
||||
@@ -212,9 +241,21 @@ func (c *WechatChannel) ParseNotify(ctx context.Context, r *http.Request, cfg *C
|
||||
mchID := cfg.Get("mch_id")
|
||||
apiV3Key := cfg.Get("api_v3_key")
|
||||
|
||||
// 平台证书由 WithWechatPayAutoAuthCipher 注册的下载器维护,直接复用其证书访问器验签
|
||||
visitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
|
||||
handler, err := notify.NewRSANotifyHandler(apiV3Key, verifiers.NewSHA256WithRSAVerifier(visitor))
|
||||
// 验签器按初始化模式选择:公钥模式用微信支付公钥,平台证书模式用下载器维护的证书访问器
|
||||
var verifier auth.Verifier
|
||||
pubKeyID := cfg.Get("pub_key_id")
|
||||
pubKeyPEM := cfg.Get("pub_key")
|
||||
if pubKeyID != "" && pubKeyPEM != "" {
|
||||
pubKey, perr := utils.LoadPublicKey(pubKeyPEM)
|
||||
if perr != nil {
|
||||
return nil, fmt.Errorf("解析微信支付公钥失败: %w", perr)
|
||||
}
|
||||
verifier = verifiers.NewSHA256WithRSAPubkeyVerifier(pubKeyID, *pubKey)
|
||||
} else {
|
||||
visitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
|
||||
verifier = verifiers.NewSHA256WithRSAVerifier(visitor)
|
||||
}
|
||||
handler, err := notify.NewRSANotifyHandler(apiV3Key, verifier)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建微信通知处理器失败: %w", err)
|
||||
}
|
||||
@@ -284,11 +325,25 @@ func (c *WechatChannel) Refund(ctx context.Context, order *models.PlatformPaymen
|
||||
}
|
||||
|
||||
func (c *WechatChannel) TestConnect(ctx context.Context, cfg *ChannelConfig) (string, error) {
|
||||
client, _, err := c.buildClient(cfg)
|
||||
client, mchID, err := c.buildClient(cfg)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 平台证书接口只做签名校验,不产生任何交易
|
||||
// 公钥模式:/v3/certificates 已对其停用,改查一笔不存在的订单验证鉴权(纯读,不产生交易)
|
||||
if cfg.Get("pub_key_id") != "" && cfg.Get("pub_key") != "" {
|
||||
_, qerr := client.Get(ctx,
|
||||
consts.WechatPayAPIServer+"/v3/pay/transactions/out-trade-no/"+connectTestOutTradeNo+"?mchid="+mchID)
|
||||
if qerr == nil {
|
||||
return "连接成功:商户私钥签名与微信支付公钥验签均通过(公钥模式)", nil
|
||||
}
|
||||
var apiErr *core.APIError
|
||||
if errors.As(qerr, &apiErr) && apiErr.StatusCode == http.StatusNotFound {
|
||||
// 订单不存在属预期结果:说明请求签名与应答验签链路均已通过
|
||||
return "连接成功:商户私钥签名与微信支付公钥验签均通过(公钥模式)", nil
|
||||
}
|
||||
return "", fmt.Errorf("微信凭证校验失败: %w", qerr)
|
||||
}
|
||||
// 平台证书模式:平台证书接口只做签名校验,不产生任何交易
|
||||
result, err := client.Get(ctx, consts.WechatPayAPIServer+"/v3/certificates")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("微信凭证校验失败: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user