This commit is contained in:
Mrx
2026-05-11 17:31:21 +08:00
parent 9e12db0cd4
commit 0d476fa477

View File

@@ -2,6 +2,7 @@ package service
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@@ -25,6 +26,14 @@ import (
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
// wxpayAPIHTTPStatus 安全读取微信支付 API 的 HTTP 状态码;本地签名/随机串失败时 result 可能为 nil。
func wxpayAPIHTTPStatus(result *core.APIResult) int {
if result == nil || result.Response == nil {
return 0
}
return result.Response.StatusCode
}
const ( const (
TradeStateSuccess = "SUCCESS" // 支付成功 TradeStateSuccess = "SUCCESS" // 支付成功
TradeStateRefund = "REFUND" // 转入退款 TradeStateRefund = "REFUND" // 转入退款
@@ -175,7 +184,7 @@ func (w *WechatPayService) CreateWechatAppOrder(ctx context.Context, amount floa
resp, result, err := svc.Prepay(ctx, payRequest) resp, result, err := svc.Prepay(ctx, payRequest)
logx.Infof("微信app支付订单resp: %+v, result: %+v, err: %+v", resp, result, err) logx.Infof("微信app支付订单resp: %+v, result: %+v, err: %+v", resp, result, err)
if err != nil { if err != nil {
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode) return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
} }
// 返回预支付交易会话标识 // 返回预支付交易会话标识
@@ -200,14 +209,23 @@ func jsapiRequestPaymentToMap(resp *jsapi.PrepayWithRequestPaymentResponse) (map
if resp.Package != nil { if resp.Package != nil {
m["package"] = *resp.Package m["package"] = *resp.Package
} }
if resp.SignType != nil { if resp.SignType != nil && *resp.SignType != "" {
m["signType"] = *resp.SignType m["signType"] = *resp.SignType
} else {
m["signType"] = "RSA"
} }
if resp.PaySign != nil { if resp.PaySign != nil {
m["paySign"] = *resp.PaySign m["paySign"] = *resp.PaySign
} }
if len(m) != 6 { var missing []string
return nil, fmt.Errorf("微信 JSAPI 调起参数不完整") for _, key := range []string{"appId", "timeStamp", "nonceStr", "package", "paySign"} {
if m[key] == "" {
missing = append(missing, key)
}
}
if len(missing) > 0 {
logx.Errorf("[WechatPay] JSAPI 调起参数缺项: missing=%v resp=%s", missing, resp.String())
return nil, fmt.Errorf("微信 JSAPI 调起参数不完整: 缺少或为空 %v", missing)
} }
return m, nil return m, nil
} }
@@ -237,7 +255,7 @@ func (w *WechatPayService) CreateWechatMiniProgramOrder(ctx context.Context, amo
resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest) resp, result, err := svc.PrepayWithRequestPayment(ctx, payRequest)
logx.Infof("微信小程序支付订单resp: %+v, result: %+v, err: %+v", resp, result, err) logx.Infof("微信小程序支付订单resp: %+v, result: %+v, err: %+v", resp, result, err)
if err != nil { if err != nil {
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode) return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
} }
return jsapiRequestPaymentToMap(resp) return jsapiRequestPaymentToMap(resp)
} }
@@ -270,7 +288,7 @@ func (w *WechatPayService) CreateWechatH5Order(ctx context.Context, amount float
logx.Infof("微信h5支付订单resp: %+v, result: %+v, err: %+v", resp, result, err) logx.Infof("微信h5支付订单resp: %+v, result: %+v, err: %+v", resp, result, err)
if err != nil { if err != nil {
logx.Infof("微信h5支付订单resp: %+v, result: %+v, err: %+v", resp, result, err) logx.Infof("微信h5支付订单resp: %+v, result: %+v, err: %+v", resp, result, err)
return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, result.Response.StatusCode) return "", fmt.Errorf("微信支付订单创建失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
} }
return jsapiRequestPaymentToMap(resp) return jsapiRequestPaymentToMap(resp)
} }
@@ -316,31 +334,28 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
if getUidErr != nil { if getUidErr != nil {
return "", getUidErr return "", getUidErr
} }
// 微信内 H5优先 wxh5_openid与公众号/网页 AppID 一致);若无则尝试 wxmini_openid走小程序 AppID 下单) // 微信内置浏览器 JSAPI 必须使用与商户公众号一致的 openidsnsapi_base / snsapi_userinfo 授权后写入 wxh5_openid
// 不可使用小程序 openid 兜底AppID 与 openid 主体不一致会导致下单失败或调起异常。
h5Auth, h5Err := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID) h5Auth, h5Err := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxh5OpenID)
if h5Err == nil { if h5Err != nil {
logx.Infof("微信h5支付订单userAuthModel(wxh5): %+v", h5Auth) if errors.Is(h5Err, model.ErrNotFound) {
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, h5Auth.AuthKey) logx.WithContext(ctx).Infof(
if err != nil { "[WechatPay] wxh5 缺少 user_auth(wxh5_openid) user_id=%d out_trade_no=%s需先走公众号网页授权(建议 scope=snsapi_base)",
return "", err userID, outTradeNo,
)
return "", fmt.Errorf("微信内支付需先完成公众号网页授权以获取 openid建议使用 snsapi_base 静默授权)")
} }
} else if h5Err == model.ErrNotFound {
miniAuth, miniErr := w.userAuthModel.FindOneByUserIdAuthType(ctx, userID, model.UserAuthTypeWxMiniOpenID)
if miniErr != nil {
return "", miniErr
}
logx.WithContext(ctx).Infof(
"[WechatPay] wxh5 无 wxh5_openid使用 wxmini_openid 下单 out_trade_no=%s user_id=%d",
outTradeNo, userID,
)
logx.Infof("微信h5支付订单userAuthModel(wxmini fallback): %+v", miniAuth)
prepayData, err = w.CreateWechatMiniProgramOrder(ctx, amount, description, outTradeNo, miniAuth.AuthKey)
if err != nil {
return "", err
}
} else {
return "", h5Err return "", h5Err
} }
if strings.TrimSpace(h5Auth.AuthKey) == "" {
logx.WithContext(ctx).Errorf("[WechatPay] wxh5_openid 记录存在但 auth_key 为空 user_id=%d", userID)
return "", fmt.Errorf("微信内支付 openid 未就绪,请重新完成公众号网页授权")
}
logx.Infof("微信h5支付订单userAuthModel(wxh5): %+v", h5Auth)
prepayData, err = w.CreateWechatH5Order(ctx, amount, description, outTradeNo, strings.TrimSpace(h5Auth.AuthKey))
if err != nil {
return "", err
}
case model.PlatformApp: case model.PlatformApp:
// 如果是 APP 平台,调用 APP 支付订单创建 // 如果是 APP 平台,调用 APP 支付订单创建
prepayData, err = w.CreateWechatAppOrder(ctx, amount, description, outTradeNo) prepayData, err = w.CreateWechatAppOrder(ctx, amount, description, outTradeNo)
@@ -355,7 +370,10 @@ func (w *WechatPayService) CreateWechatOrder(ctx context.Context, amount float64
if prepayData == nil { if prepayData == nil {
logx.WithContext(ctx).Errorf("[WechatPay] CreateWechatOrder 返回 prepayData 为 nil platform=%q", platform) logx.WithContext(ctx).Errorf("[WechatPay] CreateWechatOrder 返回 prepayData 为 nil platform=%q", platform)
} else if m, isMap := prepayData.(map[string]string); isMap { return nil, fmt.Errorf("微信支付返回数据为空 platform=%s", platform)
}
if m, isMap := prepayData.(map[string]string); isMap {
keys := make([]string, 0, len(m)) keys := make([]string, 0, len(m))
for k := range m { for k := range m {
keys = append(keys, k) keys = append(keys, k)
@@ -400,7 +418,7 @@ func (w *WechatPayService) QueryOrderStatus(ctx context.Context, transactionID s
Mchid: core.String(w.config.Wxpay.MchID), Mchid: core.String(w.config.Wxpay.MchID),
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("订单查询失败: %v, 状态码: %d", err, result.Response.StatusCode) return nil, fmt.Errorf("订单查询失败: %v, 状态码: %d", err, wxpayAPIHTTPStatus(result))
} }
return resp, nil return resp, nil