Files
kami_shop/internal/controllers/pay.go
danial 888d708857 fix(pay): 统一错误响应JSON的返回处理
- 将校验错误的错误信息改为固定提示“校验错误”
- 在解析金额失败时添加ServeJSON调用,保证响应返回
- 在金额有误时添加ServeJSON调用,保证响应返回
- 统一错误处理流程,避免遗漏响应返回导致前端异常
2025-12-16 14:54:43 +08:00

568 lines
14 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 controllers
import (
"encoding/json"
"fmt"
"github.com/beego/beego/v2/core/logs"
"github.com/duke-git/lancet/v2/convertor"
"net/url"
"shop/internal/config"
"shop/internal/models"
"shop/internal/schema/request"
"shop/internal/schema/response"
"shop/internal/service"
"shop/internal/traceRouter"
"strconv"
"strings"
"time"
"github.com/beego/beego/v2/core/validation"
"github.com/beego/beego/v2/server/web"
"go.uber.org/zap"
)
type PayController struct {
web.Controller
}
// Pay /* 支付接口 */
func (c *PayController) Pay() {
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "span", "首页")
defer span.End()
flash := web.NewFlash()
returnUrl := strings.TrimSpace(c.GetString("returnUrl"))
orderNo := strings.TrimSpace(c.GetString("orderId"))
productCode := strings.TrimSpace(c.GetString("productCode"))
recoveryType := strings.TrimSpace(c.GetString("recoveryType"))
chard := strings.TrimSpace(c.GetString("chard"))
cardNo := strings.TrimSpace(c.GetString("cardNo"))
sign := strings.TrimSpace(c.GetString("sign"))
deviceId := strings.TrimSpace(c.GetString("deviceId"))
if orderNo == "" {
flash.Error("订单号为空")
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + "0" +
"&returnUrl=" + returnUrl + "&errorMsg=" + "订单号为空!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
faceMM := strings.TrimSpace(c.GetString("factMMValue"))
if !c.judgeAmount(faceMM) {
flash.Error("金额有误")
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + "金额有误!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
m := models.OrderParams{}
m.Decrypt(ctx, sign)
if time.Since(time.Unix(m.GeneratedTime, 0)).Hours() > float64(m.Duration) {
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + "订单超时!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
if m.PayKey == "" {
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + "支付秘钥错误!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
if m.NotifyUrl == "" {
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + "通知地址为空!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
if m.OrderNo == "" {
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + "订单号为空!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
pp := map[string]string{
"recoveryType": recoveryType,
"data": chard,
"cardNo": cardNo,
}
marshal, err := json.Marshal(&pp)
if err != nil {
flash.Store(&c.Controller)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + "卡密数据解析错误!"
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
scanShop := new(service.ScanShopController)
scanShop.Params = map[string]string{
"orderPeriod": "24",
"notifyUrl": m.NotifyUrl,
"orderPrice": faceMM,
"orderNo": orderNo,
"productCode": productCode,
"exValue": string(marshal),
"ip": c.Ctx.Input.IP(),
"deviceId": deviceId,
}
res := scanShop.Shop(ctx, m.PayKey)
if res.Code == 200 {
str := "/order-confirm.html" + "?orderId=" + orderNo + "&returnUrl=" + returnUrl +
"&payKey=" + m.PayKey + "&amount=" + faceMM
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
return
}
flash.Store(&c.Controller)
errMsg := res.Msg
if errMsg == "" {
errMsg = "不存在这样的支付类型!"
}
errMsg = url.PathEscape(errMsg)
str := "/error.html" + "?orderId=" + orderNo + "&amount=" + faceMM +
"&returnUrl=" + returnUrl + "&errorMsg=" + errMsg
c.Ctx.Output.Header("Location", str)
c.Redirect(str, 302)
}
// PayWithJson 后端接口支付
func (c *PayController) PayWithJson() {
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "span", "首页")
defer span.End()
orderNo := strings.TrimSpace(c.GetString("orderId"))
if orderNo == "" {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "订单号为空",
}
_ = c.ServeJSON()
return
}
faceMM := strings.TrimSpace(c.GetString("factMMValue"))
if !c.judgeAmount(faceMM) {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "金额有误",
}
_ = c.ServeJSON()
return
}
productCode := strings.TrimSpace(c.GetString("productCode"))
recoveryType := strings.TrimSpace(c.GetString("recoveryType"))
chard := strings.TrimSpace(c.GetString("chard"))
cardNo := strings.TrimSpace(c.GetString("cardNo"))
sign := strings.TrimSpace(c.GetString("sign"))
deviceId := strings.TrimSpace(c.GetString("deviceId"))
m, err := service.VerifyPaySign(ctx, sign)
if err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: err.Error(),
}
_ = c.ServeJSON()
return
}
if m == nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "支付秘钥错误",
}
_ = c.ServeJSON()
return
}
ok2, _ := service.Pay(ctx, &models.OriginalJdParams{
OrderId: orderNo,
FactMMValue: faceMM,
ProductCode: productCode,
RecoveryType: recoveryType,
Chard: chard,
CardNo: cardNo,
DeviceId: deviceId,
NotifyUrl: m.NotifyUrl,
}, m, c.Ctx.Input.IP())
if ok2 {
c.Data["json"] = response.CommonResponse{
Code: 0,
Msg: "成功!",
}
_ = c.ServeJSON()
return
}
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "内部错误",
}
_ = c.ServeJSON()
}
func (c *PayController) judgeAmount(amount string) bool {
_, err := strconv.ParseFloat(amount, 64)
if err != nil {
traceRouter.Logger.WithContext(c.Ctx.Request.Context()).Info("输入金额有误", zap.String("amount", amount))
//logs.Error("输入金额有误")
return false
}
return true
}
// PayOriginalJD /* 支付接口 */
func (c *PayController) PayOriginalJD() {
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "PayController", "PayController.ayOriginalJD")
defer span.End()
input := models.OriginalJdReq{}
err := c.BindJSON(&input)
if err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "JSON解析失败",
}
_ = c.ServeJSON()
return
}
valid := validation.Validation{}
ok, err := valid.Valid(input)
if err != nil || !ok {
traceRouter.Logger.WithContext(ctx).Error("解析失败", zap.Error(err))
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "解析失败",
}
_ = c.ServeJSON()
return
}
// 校验sign
m, err := service.VerifyPaySign(ctx, input.Sign)
if err != nil || m == nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: err.Error(),
}
_ = c.ServeJSON()
return
}
faceMMValue, err := strconv.ParseFloat(input.MMValue, 64)
if err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "金额有误",
}
_ = c.ServeJSON()
return
}
wxPay, err := service.PayWithJd(ctx, input.OrderId, "cTrip", faceMMValue)
if err != nil {
traceRouter.Logger.WithContext(ctx).Error("微信支付拉单错误!", zap.String("orderNo", m.OrderNo),
zap.String("error", err.Error()),
)
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "微信支付拉单错误!",
}
_ = c.ServeJSON()
return
}
//去掉域名
wxPay = strings.ReplaceAll(wxPay, "https://wx.tenpay.com", "")
c.Data["json"] = response.CommonResponse{
Code: 0,
Msg: "成功!",
Data: map[string]string{"wxPay": wxPay},
}
_ = c.ServeJSON()
}
// PayOriginalAliPay /* 支付接口 */
func (c *PayController) PayOriginalAliPay() {
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "PayController", "PayController.PayOriginalAliPay")
defer span.End()
input := models.OriginalJdReq{}
err := c.BindJSON(&input)
if err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "JSON解析失败",
}
_ = c.ServeJSON()
return
}
valid := validation.Validation{}
ok, err := valid.Valid(input)
if err != nil || !ok {
traceRouter.Logger.WithContext(ctx).Error("解析失败", zap.Error(err))
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "解析失败",
}
_ = c.ServeJSON()
return
}
// 校验sign
m, err := service.VerifyPaySign(ctx, input.Sign)
if err != nil || m == nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: err.Error(),
}
_ = c.ServeJSON()
return
}
faceMMValue, err := strconv.ParseFloat(input.MMValue, 64)
if err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "金额有误",
}
_ = c.ServeJSON()
return
}
_, err = service.PayWithAliPay(ctx, input.OrderId, faceMMValue)
if err != nil {
traceRouter.Logger.WithContext(ctx).Error("支付宝支付拉单错误!", zap.String("orderNo", m.OrderNo),
zap.String("error", err.Error()),
)
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "支付宝支付拉单错误!",
}
_ = c.ServeJSON()
return
}
jumpAddr := config.GetShopUrl() + "/alipay/pay?orderId=" + input.OrderId + "&amount=" + convertor.ToString(faceMMValue)
//url编码
logs.Info("alipayUrl", jumpAddr)
c.Data["json"] = response.CommonResponse{
Code: 0,
Msg: "成功!",
Data: map[string]string{"alipayUrl": fmt.Sprintf("alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=%s", url.QueryEscape(jumpAddr))},
}
_ = c.ServeJSON()
}
// PayV2 新版本JSON支付接口支持JSON请求和响应
func (c *PayController) PayV2() {
ctx, span := traceRouter.CreateSpan(c.Ctx.Request.Context(), "span", "JSON支付接口")
defer span.End()
var req request.PayRequest
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "JSON解析失败",
}
_ = c.ServeJSON()
return
}
// 验证必填字段
if req.OrderId == "" {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "订单号为空",
}
_ = c.ServeJSON()
return
}
if !c.judgeAmount(req.FactMMValue) {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "金额有误",
}
_ = c.ServeJSON()
return
}
m, err := service.VerifyPaySign(ctx, req.Sign)
if err != nil || m == nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "校验错误",
}
_ = c.ServeJSON()
return
}
//req.FactMMValue
factValue, err := convertor.ToFloat(req.FactMMValue)
if err != nil {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "解析金额失败",
}
_ = c.ServeJSON()
}
if m.ShowMMValue != factValue {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "金额有误",
}
_ = c.ServeJSON()
return
}
if m.OrderNo != req.OrderId {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "校验失败",
}
_ = c.ServeJSON()
return
}
// 验证订单超时
if time.Since(time.Unix(m.GeneratedTime, 0)).Hours() > float64(m.Duration) {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "订单超时!",
}
_ = c.ServeJSON()
return
}
// 验证支付密钥
if m.PayKey == "" {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "支付秘钥错误!",
}
_ = c.ServeJSON()
return
}
// 验证通知地址
if m.NotifyUrl == "" {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "通知地址为空!",
}
_ = c.ServeJSON()
return
}
// 验证订单号
if m.OrderNo == "" {
c.Data["json"] = response.CommonResponse{
Code: -1,
Msg: "订单号为空!",
}
_ = c.ServeJSON()
return
}
// 构建卡密数据
pp := map[string]string{
"recoveryType": req.RecoveryType,
"data": req.Chard,
"cardNo": req.CardNo,
}
marshal, err := json.Marshal(&pp)
if err != nil {
errHtml := "/error.html" + "?orderId=" + req.OrderId + "&amount=" + req.FactMMValue +
"&returnUrl=" + req.ReturnUrl + "&errorMsg=" + url.PathEscape("卡密数据解析错误!")
c.Data["json"] = response.CommonResponse{
Code: 0,
Msg: "支付成功",
Data: map[string]string{
"orderId": req.OrderId,
"returnUrl": req.ReturnUrl,
"payKey": m.PayKey,
"amount": req.FactMMValue,
"redirectUrl": errHtml,
},
}
_ = c.ServeJSON()
return
}
// 调用支付服务
scanShop := new(service.ScanShopController)
scanShop.Params = map[string]string{
"orderPeriod": "24",
"notifyUrl": m.NotifyUrl,
"orderPrice": req.FactMMValue,
"orderNo": req.OrderId,
"productCode": req.ProductCode,
"exValue": string(marshal),
"ip": c.Ctx.Input.IP(),
"deviceId": req.DeviceId,
}
res := scanShop.Shop(ctx, m.PayKey)
if res.Code == 200 {
// 返回成功响应包含订单确认页面URL
c.Data["json"] = response.CommonResponse{
Code: 0,
Msg: "支付成功",
Data: map[string]string{
"orderId": req.OrderId,
"returnUrl": req.ReturnUrl,
"payKey": m.PayKey,
"amount": req.FactMMValue,
"redirectUrl": "/order-confirm.html?orderId=" + req.OrderId + "&returnUrl=" + req.ReturnUrl + "&payKey=" + m.PayKey + "&amount=" + req.FactMMValue,
},
}
_ = c.ServeJSON()
return
}
// 处理错误情况
errMsg := res.Msg
if errMsg == "" {
errMsg = "不存在这样的支付类型!"
}
errHtml := "/error.html" + "?orderId=" + req.OrderId + "&amount=" + req.FactMMValue +
"&returnUrl=" + req.ReturnUrl + "&errorMsg=" + url.PathEscape(errMsg)
c.Data["json"] = response.CommonResponse{
Code: 0,
Msg: "支付成功",
Data: map[string]string{
"orderId": req.OrderId,
"returnUrl": req.ReturnUrl,
"payKey": m.PayKey,
"amount": req.FactMMValue,
"redirectUrl": errHtml,
},
}
_ = c.ServeJSON()
}