f
This commit is contained in:
@@ -2,15 +2,19 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
"qnc-server/app/main/api/internal/config"
|
||||
"qnc-server/app/main/model"
|
||||
"qnc-server/common/ctxdata"
|
||||
"qnc-server/pkg/lzkit/lzUtils"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
|
||||
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
|
||||
@@ -251,9 +255,16 @@ func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float
|
||||
}
|
||||
|
||||
// CreateWechatOrder 创建微信支付订单(集成 APP、H5、小程序)
|
||||
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string) (interface{}, error) {
|
||||
// 根据 ctx 中的 platform 判断平台
|
||||
platform := ctx.Value("platform").(string)
|
||||
func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64, description string, outTradeNo string, code string) (interface{}, error) {
|
||||
// 根据 ctx 中的 platform 判断平台(请求头 X-Platform: wxmini / wxh5 / app)
|
||||
platformValue := ctx.Value("platform")
|
||||
if platformValue == nil {
|
||||
return "", fmt.Errorf("平台信息不存在,请检查请求头 X-Platform")
|
||||
}
|
||||
platform, ok := platformValue.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("平台信息格式错误")
|
||||
}
|
||||
|
||||
var prepayData interface{}
|
||||
var err error
|
||||
@@ -266,7 +277,30 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
||||
}
|
||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
|
||||
if findAuthModelErr != nil {
|
||||
return "", findAuthModelErr
|
||||
if errors.Is(findAuthModelErr, model.ErrNotFound) {
|
||||
// 用户未绑定微信,尝试通过 code 自动绑定
|
||||
if code == "" {
|
||||
return "", fmt.Errorf("用户未绑定微信小程序账号,请先完成微信登录或提供授权码")
|
||||
}
|
||||
// 通过 code 获取 OpenID
|
||||
openid, getOpenidErr := w.getWechatMiniOpenID(ctx, code)
|
||||
if getOpenidErr != nil {
|
||||
return "", fmt.Errorf("获取微信小程序OpenID失败: %v", getOpenidErr)
|
||||
}
|
||||
// 自动绑定微信账号
|
||||
bindErr := w.bindWechatAuth(ctx, userID, model.UserAuthTypeWxMiniOpenID, openid)
|
||||
if bindErr != nil {
|
||||
return "", fmt.Errorf("绑定微信小程序账号失败: %v", bindErr)
|
||||
}
|
||||
// 重新查询绑定后的认证信息
|
||||
userAuthModel, findAuthModelErr = w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
|
||||
if findAuthModelErr != nil {
|
||||
return "", fmt.Errorf("查询绑定后的微信认证信息失败: %v", findAuthModelErr)
|
||||
}
|
||||
logx.Infof("用户 %s 已自动绑定微信小程序账号,OpenID: %s", userID, openid)
|
||||
} else {
|
||||
return "", fmt.Errorf("查询用户微信认证信息失败: %v", findAuthModelErr)
|
||||
}
|
||||
}
|
||||
prepayData, err = w.CreateWechatMiniProgramOrder(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
||||
if err != nil {
|
||||
@@ -279,7 +313,30 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
|
||||
}
|
||||
userAuthModel, findAuthModelErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
||||
if findAuthModelErr != nil {
|
||||
return "", findAuthModelErr
|
||||
if errors.Is(findAuthModelErr, model.ErrNotFound) {
|
||||
// 用户未绑定微信,尝试通过 code 自动绑定
|
||||
if code == "" {
|
||||
return "", fmt.Errorf("用户未绑定微信H5账号,请先完成微信登录或提供授权码")
|
||||
}
|
||||
// 通过 code 获取 OpenID
|
||||
openid, getOpenidErr := w.getWechatH5OpenID(ctx, code)
|
||||
if getOpenidErr != nil {
|
||||
return "", fmt.Errorf("获取微信H5 OpenID失败: %v", getOpenidErr)
|
||||
}
|
||||
// 自动绑定微信账号
|
||||
bindErr := w.bindWechatAuth(ctx, userID, model.UserAuthTypeWxh5OpenID, openid)
|
||||
if bindErr != nil {
|
||||
return "", fmt.Errorf("绑定微信H5账号失败: %v", bindErr)
|
||||
}
|
||||
// 重新查询绑定后的认证信息
|
||||
userAuthModel, findAuthModelErr = w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
|
||||
if findAuthModelErr != nil {
|
||||
return "", fmt.Errorf("查询绑定后的微信认证信息失败: %v", findAuthModelErr)
|
||||
}
|
||||
logx.Infof("用户 %s 已自动绑定微信H5账号,OpenID: %s", userID, openid)
|
||||
} else {
|
||||
return "", fmt.Errorf("查询用户微信认证信息失败: %v", findAuthModelErr)
|
||||
}
|
||||
}
|
||||
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, userAuthModel.AuthKey)
|
||||
if err != nil {
|
||||
@@ -384,3 +441,112 @@ func (w *WechatPayService) GenerateOutTradeNo() string {
|
||||
|
||||
return combined
|
||||
}
|
||||
|
||||
// getWechatMiniOpenID 通过 code 获取微信小程序 OpenID
|
||||
func (w *WechatPayService) getWechatMiniOpenID(ctx context.Context, code string) (string, error) {
|
||||
appID := w.config.WechatMini.AppID
|
||||
appSecret := w.config.WechatMini.AppSecret
|
||||
url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appID, appSecret, code)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("请求微信API失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
var data struct {
|
||||
Openid string `json:"openid"`
|
||||
ErrCode int `json:"errcode,omitempty"`
|
||||
ErrMsg string `json:"errmsg,omitempty"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &data); err != nil {
|
||||
return "", fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
if data.ErrCode != 0 {
|
||||
return "", fmt.Errorf("微信API返回错误: errcode=%d, errmsg=%s", data.ErrCode, data.ErrMsg)
|
||||
}
|
||||
|
||||
if data.Openid == "" {
|
||||
return "", fmt.Errorf("openid为空")
|
||||
}
|
||||
|
||||
return data.Openid, nil
|
||||
}
|
||||
|
||||
// getWechatH5OpenID 通过 code 获取微信H5 OpenID
|
||||
func (w *WechatPayService) getWechatH5OpenID(ctx context.Context, code string) (string, error) {
|
||||
appID := w.config.WechatH5.AppID
|
||||
appSecret := w.config.WechatH5.AppSecret
|
||||
url := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", appID, appSecret, code)
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("请求微信API失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
var data struct {
|
||||
Openid string `json:"openid"`
|
||||
ErrCode int `json:"errcode,omitempty"`
|
||||
ErrMsg string `json:"errmsg,omitempty"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, &data); err != nil {
|
||||
return "", fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
if data.ErrCode != 0 {
|
||||
return "", fmt.Errorf("微信API返回错误: errcode=%d, errmsg=%s", data.ErrCode, data.ErrMsg)
|
||||
}
|
||||
|
||||
if data.Openid == "" {
|
||||
return "", fmt.Errorf("openid为空")
|
||||
}
|
||||
|
||||
return data.Openid, nil
|
||||
}
|
||||
|
||||
// bindWechatAuth 绑定微信认证信息到用户账号
|
||||
func (w *WechatPayService) bindWechatAuth(ctx context.Context, userID string, authType string, openid string) error {
|
||||
// 检查该 OpenID 是否已被其他用户绑定
|
||||
existingAuth, err := w.userAuthModel.FindOneByAuthTypeAuthKey(ctx, authType, openid)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return fmt.Errorf("查询认证信息失败: %v", err)
|
||||
}
|
||||
|
||||
if existingAuth != nil {
|
||||
// 如果 OpenID 已被其他用户绑定,检查是否是当前用户
|
||||
if existingAuth.UserId != userID {
|
||||
return fmt.Errorf("该微信账号已被其他用户绑定")
|
||||
}
|
||||
// 如果已经是当前用户绑定的,直接返回成功
|
||||
return nil
|
||||
}
|
||||
|
||||
// 创建新的认证记录
|
||||
userAuth := &model.UserAuth{
|
||||
Id: uuid.NewString(),
|
||||
UserId: userID,
|
||||
AuthType: authType,
|
||||
AuthKey: openid,
|
||||
}
|
||||
|
||||
_, err = w.userAuthModel.Insert(ctx, nil, userAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建认证记录失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user