支付模块重构完成

This commit is contained in:
RockYang
2025-08-30 16:27:39 +08:00
parent 3a6f8ccc16
commit 3c065b99fb
17 changed files with 661 additions and 317 deletions

View File

@@ -11,9 +11,8 @@ type OrderStatus int
const (
OrderNotPaid = OrderStatus(0)
OrderScanned = OrderStatus(1) // 已扫码
OrderPaidSuccess = OrderStatus(2)
OrderPaidFailed = OrderStatus(3)
OrderPaidSuccess = OrderStatus(2) // 已支付
OrderPaidFailed = OrderStatus(3) // 已关闭
)
type OrderRemark struct {

View File

@@ -81,19 +81,6 @@ func (h *ManagerHandler) Login(c *gin.Context) {
return
}
// if h.App.SysConfig.Base.EnabledVerify {
// var check bool
// if data.X != 0 {
// check = h.captcha.SlideCheck(data)
// } else {
// check = h.captcha.Check(data)
// }
// if !check {
// resp.ERROR(c, "请先完人机验证")
// return
// }
// }
var manager model.AdminUser
res := h.DB.Model(&model.AdminUser{}).Where("username = ?", data.Username).First(&manager)
if res.Error != nil {

View File

@@ -15,9 +15,10 @@ import (
"geekai/store/vo"
"geekai/utils"
"geekai/utils/resp"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"time"
)
type ProductHandler struct {
@@ -43,9 +44,7 @@ func (h *ProductHandler) Save(c *gin.Context) {
Id uint `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Discount float64 `json:"discount"`
Enabled bool `json:"enabled"`
Days int `json:"days"`
Power int `json:"power"`
CreatedAt int64 `json:"created_at"`
}
@@ -55,12 +54,10 @@ func (h *ProductHandler) Save(c *gin.Context) {
}
item := model.Product{
Name: data.Name,
Price: data.Price,
Discount: data.Discount,
Days: data.Days,
Power: data.Power,
Enabled: data.Enabled}
Name: data.Name,
Price: data.Price,
Power: data.Power,
Enabled: data.Enabled}
item.Id = data.Id
if item.Id > 0 {
item.CreatedAt = time.Unix(data.CreatedAt, 0)

View File

@@ -15,7 +15,6 @@ import (
"geekai/store/vo"
"geekai/utils"
"geekai/utils/resp"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
@@ -96,17 +95,8 @@ func (h *OrderHandler) Query(c *gin.Context) {
return
}
counter := 0
for {
time.Sleep(time.Second)
var item model.Order
h.DB.Where("order_no = ?", orderNo).First(&item)
if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status {
order.Status = item.Status
break
}
counter++
}
var item model.Order
h.DB.Where("order_no = ?", orderNo).First(&item)
resp.SUCCESS(c, gin.H{"status": order.Status})
}

View File

@@ -9,7 +9,6 @@ package handler
import (
"embed"
"errors"
"fmt"
"geekai/core"
"geekai/core/middleware"
@@ -77,17 +76,14 @@ func (h *PaymentHandler) RegisterRoutes() {
// 支付回调接口(公开)
rg.POST("notify/alipay", h.AlipayNotify)
rg.GET("notify/geek", h.GeekPayNotify)
rg.POST("notify/wechat", h.WechatPayNotify)
rg.GET("notify/epay", h.EPayNotify)
rg.POST("notify/wxpay", h.WxpayNotify)
// 需要用户登录的接口
rg.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis))
{
rg.POST("create", h.Pay)
rg.POST("create", h.CreateOrder)
}
// 同步订单状态
h.StartSyncOrders()
}
func (h *PaymentHandler) StartSyncOrders() {
@@ -116,10 +112,11 @@ func (h *PaymentHandler) SyncOrders() error {
}
for _, order := range orders {
// 超时15分钟的订单直接标记为已关闭
//超时15分钟的订单直接标记为已关闭
if time.Now().After(order.CreatedAt.Add(time.Minute * 15)) {
h.DB.Model(&model.Order{}).Where("id", order.Id).Update("checked", true)
return errors.New("订单超时")
logger.Errorf("订单超时:%v", order)
continue
}
// 查询订单状态
var res payment.OrderInfo
@@ -127,18 +124,22 @@ func (h *PaymentHandler) SyncOrders() error {
case payment.PayChannelEpay:
res, err = h.epayService.Query(order.OrderNo)
if err != nil {
return fmt.Errorf("error with query order info: %v", err)
logger.Errorf("error with query order info: %v", err)
continue
}
// 微信支付
case payment.PayChannelWX:
res, err = h.wxpayService.Query(order.OrderNo)
logger.Debugf("微信支付订单状态:%+v", res)
if err != nil {
return fmt.Errorf("error with query order info: %v", err)
logger.Errorf("error with query order info: %v", err)
continue
}
case payment.PayChannelAL:
res, err = h.alipayService.Query(order.OrderNo)
if err != nil {
return fmt.Errorf("error with query order info: %v", err)
logger.Errorf("error with query order info: %v", err)
continue
}
}
@@ -148,24 +149,26 @@ func (h *PaymentHandler) SyncOrders() error {
"checked": true,
"status": types.OrderPaidFailed,
})
return errors.New("订单已关闭")
logger.Errorf("订单已关闭:%v", order)
continue
}
// 订单未支付,不处理,继续轮询
if !res.Success() {
return nil
continue
}
// 订单支付成功
err = h.paySuccess(res)
if err != nil {
return fmt.Errorf("error with deal order: %v", err)
logger.Errorf("error with deal order: %v", err)
continue
}
}
return nil
}
func (h *PaymentHandler) Pay(c *gin.Context) {
func (h *PaymentHandler) CreateOrder(c *gin.Context) {
var data struct {
PayWay string `json:"pay_way,omitempty"` // 支付方式:支付宝,微信
Pid int `json:"pid,omitempty"`
@@ -210,7 +213,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
if h.config.WxPay.Domain != "" {
data.Domain = h.config.WxPay.Domain
}
notifyURL = fmt.Sprintf("%s/api/payment/notify/wechat", data.Domain)
notifyURL = fmt.Sprintf("%s/api/payment/notify/wxpay", data.Domain)
payURL, err = h.wxpayService.Pay(payment.PayRequest{
OutTradeNo: orderNo,
TotalFee: fmt.Sprintf("%d", int(amount*100)),
@@ -230,7 +233,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
if h.config.Epay.Domain != "" {
data.Domain = h.config.Epay.Domain
}
notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Domain)
notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain)
params := payment.PayRequest{
OutTradeNo: orderNo,
Subject: product.Name,
@@ -280,7 +283,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
if h.config.Epay.Domain != "" {
data.Domain = h.config.Epay.Domain
}
notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Domain)
notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain)
params := payment.PayRequest{
OutTradeNo: orderNo,
Subject: product.Name,
@@ -309,7 +312,6 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
// 创建订单
remark := types.OrderRemark{
Days: product.Days,
Power: product.Power,
Name: product.Name,
Price: product.Price,
@@ -377,7 +379,7 @@ func (h *PaymentHandler) paySuccess(info payment.OrderInfo) error {
order.Status = types.OrderPaidSuccess
order.TradeNo = info.TradeId
order.Checked = true
err = h.DB.Updates(&order).Error
err = h.DB.Debug().Updates(&order).Error
if err != nil {
return fmt.Errorf("error with update order info: %v", err)
}
@@ -418,14 +420,14 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
c.String(http.StatusOK, "success")
}
// GeekPayNotify 支付异步回调
func (h *PaymentHandler) GeekPayNotify(c *gin.Context) {
// EPayNotify 易支付支付异步回调
func (h *PaymentHandler) EPayNotify(c *gin.Context) {
var params = make(map[string]string)
for k := range c.Request.URL.Query() {
params[k] = c.Query(k)
}
logger.Infof("收到GeekPay订单支付回调:%+v", params)
logger.Infof("收到易支付订单支付回调:%+v", params)
// 检查支付状态, 如果未支付,则返回成功
if params["trade_status"] != "TRADE_SUCCESS" {
c.String(http.StatusOK, "success")
@@ -456,8 +458,8 @@ func (h *PaymentHandler) GeekPayNotify(c *gin.Context) {
c.String(http.StatusOK, "success")
}
// WechatPayNotify 微信商户支付异步回调
func (h *PaymentHandler) WechatPayNotify(c *gin.Context) {
// WxpayNotify 微信商户支付异步回调
func (h *PaymentHandler) WxpayNotify(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
c.String(http.StatusOK, "fail")

View File

@@ -303,6 +303,7 @@ func main() {
}),
fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) {
h.RegisterRoutes()
h.StartSyncOrders()
}),
fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) {
h.RegisterRoutes()

View File

@@ -118,6 +118,18 @@ func (s *MigrationService) TableMigration() {
if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") {
s.db.Migrator().DropColumn(&model.ChatModel{}, "description")
}
if s.db.Migrator().HasColumn(&model.Product{}, "discount") {
s.db.Migrator().DropColumn(&model.Product{}, "discount")
}
if s.db.Migrator().HasColumn(&model.Product{}, "days") {
s.db.Migrator().DropColumn(&model.Product{}, "days")
}
if s.db.Migrator().HasColumn(&model.Product{}, "app_url") {
s.db.Migrator().DropColumn(&model.Product{}, "app_url")
}
if s.db.Migrator().HasColumn(&model.Product{}, "url") {
s.db.Migrator().DropColumn(&model.Product{}, "url")
}
}
// 迁移配置数据

View File

@@ -74,6 +74,7 @@ func (s *WxPayService) Pay(params PayRequest) (string, error) {
bm.Set("total", utils.IntValue(params.TotalFee, 0)).
Set("currency", "CNY")
})
logger.Debugf("wxpay params: %+v", bm)
if params.Device == "mobile" {
bm.SetBodyMap("scene_info", func(bm gopay.BodyMap) {
bm.Set("payer_client_ip", params.ClientIP)

View File

@@ -17,7 +17,7 @@ type Order struct {
Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;default:0.00;comment:订单金额" json:"amount"`
Status types.OrderStatus `gorm:"column:status;type:tinyint(1);not null;default:0;comment:订单状态0待支付1已扫码2支付成功" json:"status"`
Remark string `gorm:"column:remark;type:varchar(255);not null;comment:备注" json:"remark"`
PayTime int64 `gorm:"column:pay_time;type:int;comment:支付时间" json:"pay_time"`
PayTime int64 `gorm:"column:pay_time;type:int(11);comment:支付时间" json:"pay_time"`
PayWay string `gorm:"column:pay_way;type:varchar(20);not null;comment:支付方式" json:"pay_way"`
Channel string `gorm:"column:channel;type:varchar(30);not null;comment:支付类型渠道:支付宝,微信,聚合支付"` // 支付类型渠道
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`

View File

@@ -6,19 +6,15 @@ import (
// Product 充值产品
type Product struct {
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
Name string `gorm:"column:name;type:varchar(30);not null;comment:名称" json:"name"`
Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"`
Discount float64 `gorm:"column:discount;type:decimal(10,2);not null;default:0.00;comment:优惠金额" json:"discount"`
Days int `gorm:"column:days;type:smallint;not null;default:0;comment:延长天数" json:"days"`
Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"`
Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"`
Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"`
SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"`
AppUrl string `gorm:"column:app_url;type:varchar(255);comment:App跳转地址" json:"app_url"`
Url string `gorm:"column:url;type:varchar(255);comment:跳转地址" json:"url"`
Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"`
Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"`
Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"`
Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"`
SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"`
}
func (m *Product) TableName() string {

View File

@@ -17,8 +17,9 @@ import (
"time"
"unicode"
"golang.org/x/crypto/sha3"
rand2 "math/rand"
"golang.org/x/crypto/sha3"
)
// RandString generate rand string with specified length
@@ -72,7 +73,16 @@ func Str2stamp(str string) int64 {
return 0
}
layout := "2006-01-02 15:04:05"
var layout string
if strings.Contains(str, "T") {
layout = "2006-01-02T15:04:05-07:00"
} else {
if len(str) < 12 {
str = str + " 00:00:00"
}
layout = "2006-01-02 15:04:05"
}
t, err := time.ParseInLocation(layout, str, time.Local)
if err != nil {
return 0