geekai/api/handler/payment_handler.go

274 lines
6.5 KiB
Go
Raw Normal View History

2023-11-06 17:55:46 +08:00
package handler
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/service"
"chatplus/service/payment"
"chatplus/store/model"
"chatplus/utils"
"chatplus/utils/resp"
"embed"
"encoding/base64"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"net/url"
"sync"
"time"
)
2023-11-08 17:48:07 +08:00
const (
PayWayAlipay = "支付宝"
PayWayWechat = "微信支付"
)
2023-11-06 17:55:46 +08:00
// PaymentHandler 支付服务回调 handler
type PaymentHandler struct {
BaseHandler
alipayService *payment.AlipayService
snowflake *service.Snowflake
db *gorm.DB
fs embed.FS
lock sync.Mutex
}
func NewPaymentHandler(server *core.AppServer, alipayService *payment.AlipayService, snowflake *service.Snowflake, db *gorm.DB, fs embed.FS) *PaymentHandler {
h := PaymentHandler{lock: sync.Mutex{}}
h.App = server
h.alipayService = alipayService
h.snowflake = snowflake
h.db = db
h.fs = fs
return &h
}
func (h *PaymentHandler) Alipay(c *gin.Context) {
orderNo := h.GetTrim(c, "order_no")
if orderNo == "" {
resp.ERROR(c, types.InvalidArgs)
return
}
var order model.Order
res := h.db.Where("order_no = ?", orderNo).First(&order)
if res.Error != nil {
resp.ERROR(c, "Order not found")
return
}
// 更新扫码状态
h.db.Model(&order).UpdateColumn("status", types.OrderScanned)
// 生成支付链接
notifyURL := h.App.Config.AlipayConfig.NotifyURL
returnURL := "" // 关闭同步回跳
2023-11-06 17:55:46 +08:00
amount := fmt.Sprintf("%.2f", order.Amount)
uri, err := h.alipayService.PayUrlMobile(order.OrderNo, notifyURL, returnURL, amount, order.Subject)
if err != nil {
resp.ERROR(c, "error with generate pay url: "+err.Error())
return
}
c.Redirect(302, uri)
}
// OrderQuery 清单状态查询
func (h *PaymentHandler) OrderQuery(c *gin.Context) {
var data struct {
OrderNo string `json:"order_no"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
var order model.Order
res := h.db.Where("order_no = ?", data.OrderNo).First(&order)
if res.Error != nil {
resp.ERROR(c, "Order not found")
return
}
if order.Status == types.OrderPaidSuccess {
resp.SUCCESS(c, gin.H{"status": order.Status})
return
}
counter := 0
for {
time.Sleep(time.Second)
var item model.Order
h.db.Where("order_no = ?", data.OrderNo).First(&item)
if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status {
order.Status = item.Status
break
}
counter++
}
resp.SUCCESS(c, gin.H{"status": order.Status})
}
// AlipayQrcode 生成支付宝支付 URL 二维码
func (h *PaymentHandler) AlipayQrcode(c *gin.Context) {
if !h.App.SysConfig.EnabledAlipay || h.alipayService == nil {
resp.ERROR(c, "当前支付通道已经关闭,请联系管理员开通!")
return
}
2023-11-06 17:55:46 +08:00
var data struct {
ProductId uint `json:"product_id"`
2023-11-08 17:48:07 +08:00
UserId int `json:"user_id"`
2023-11-06 17:55:46 +08:00
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
var product model.Product
res := h.db.First(&product, data.ProductId)
if res.Error != nil {
resp.ERROR(c, "Product not found")
return
}
orderNo, err := h.snowflake.Next()
if err != nil {
resp.ERROR(c, "error with generate trade no: "+err.Error())
return
}
2023-11-08 17:48:07 +08:00
var user model.User
res = h.db.First(&user, data.UserId)
if res.Error != nil {
resp.ERROR(c, "Invalid user ID")
2023-11-06 17:55:46 +08:00
return
}
// 创建订单
remark := types.OrderRemark{
Days: product.Days,
Calls: product.Calls,
Name: product.Name,
Price: product.Price,
Discount: product.Discount,
}
order := model.Order{
UserId: user.Id,
Mobile: user.Mobile,
ProductId: product.Id,
OrderNo: orderNo,
Subject: product.Name,
Amount: product.Price - product.Discount,
Status: types.OrderNotPaid,
2023-11-08 17:48:07 +08:00
PayWay: PayWayAlipay,
2023-11-06 17:55:46 +08:00
Remark: utils.JsonEncode(remark),
}
res = h.db.Create(&order)
if res.Error != nil {
resp.ERROR(c, "error with create order: "+res.Error.Error())
return
}
// 生成二维码图片
file, err := h.fs.Open("res/img/alipay.jpg")
if err != nil {
resp.ERROR(c, err.Error())
return
}
parse, err := url.Parse(h.App.Config.AlipayConfig.NotifyURL)
if err != nil {
resp.ERROR(c, err.Error())
return
}
imageURL := fmt.Sprintf("%s://%s/api/payment/alipay?order_no=%s", parse.Scheme, parse.Host, orderNo)
imgData, err := utils.GenQrcode(imageURL, 400, file)
if err != nil {
resp.ERROR(c, err.Error())
return
}
imgDataBase64 := base64.StdEncoding.EncodeToString(imgData)
resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": imageURL})
}
func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
err := c.Request.ParseForm()
if err != nil {
c.String(http.StatusOK, "fail")
return
}
// TODO这里最好用支付宝的公钥签名签证一下交易真假
//res := h.alipayService.TradeVerify(c.Request.Form)
r := h.alipayService.TradeQuery(c.Request.Form.Get("out_trade_no"))
logger.Infof("验证支付结果:%+v", r)
if !r.Success() {
c.String(http.StatusOK, "fail")
return
}
h.lock.Lock()
defer h.lock.Unlock()
var order model.Order
res := h.db.Where("order_no = ?", r.OutTradeNo).First(&order)
if res.Error != nil {
logger.Error(res.Error)
c.String(http.StatusOK, "fail")
return
}
var user model.User
res = h.db.First(&user, order.UserId)
if res.Error != nil {
logger.Error(res.Error)
c.String(http.StatusOK, "fail")
return
}
var remark types.OrderRemark
err = utils.JsonDecode(order.Remark, &remark)
if err != nil {
logger.Error(res.Error)
c.String(http.StatusOK, "fail")
return
}
// 1. 点卡days == 0, calls > 0
// 2. vip 套餐days > 0, calls == 0
if remark.Days > 0 {
if user.ExpiredTime > time.Now().Unix() {
user.ExpiredTime = time.Unix(user.ExpiredTime, 0).AddDate(0, 0, remark.Days).Unix()
} else {
user.ExpiredTime = time.Now().AddDate(0, 0, remark.Days).Unix()
}
user.Vip = true
2023-11-08 17:48:07 +08:00
2023-11-06 17:55:46 +08:00
} else if !user.Vip { // 充值点卡的非 VIP 用户
user.ExpiredTime = time.Now().AddDate(0, 0, 30).Unix()
}
if remark.Calls > 0 { // 充值点卡
user.Calls += remark.Calls
} else {
user.Calls += h.App.SysConfig.VipMonthCalls
}
// 更新用户信息
res = h.db.Updates(&user)
if res.Error != nil {
logger.Error(res.Error)
c.String(http.StatusOK, "fail")
return
}
// 更新订单状态
order.PayTime = time.Now().Unix()
order.Status = types.OrderPaidSuccess
h.db.Updates(&order)
// 更新产品销量
h.db.Model(&model.Product{}).Where("id = ?", order.ProductId).UpdateColumn("sales", gorm.Expr("sales + ?", 1))
c.String(http.StatusOK, "success")
}