Files
platform/web/handlers/trade.go

213 lines
4.6 KiB
Go

package handlers
import (
"fmt"
"github.com/shopspring/decimal"
"github.com/valyala/fasthttp/fasthttpadaptor"
"log/slog"
"net/http"
"platform/web/auth"
trade2 "platform/web/domains/trade"
g "platform/web/globals"
q "platform/web/queries"
s "platform/web/services"
"platform/web/tasks"
"time"
"github.com/gofiber/fiber/v2"
"github.com/smartwalle/alipay/v3"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
)
func TradeCheckSSE(c *fiber.Ctx) error {
// 设置响应头
c.Set("Content-Type", "text/event-stream")
c.Set("Cache-Control", "no-cache")
c.Set("Connection", "keep-alive")
return nil
}
type TradeCheckReq struct {
tasks.CancelTradeData
}
func TradeCancelByTask(c *fiber.Ctx) error {
// 检查权限
_, err := auth.Protect(c, []auth.PayloadType{auth.PayloadInternalServer}, []string{})
if err != nil {
return err
}
// 解析请求参数
req := new(TradeCheckReq)
if err := c.BodyParser(req); err != nil {
return err
}
// 检查订单状态
err = s.Trade.CheckTradeIfCanceled(&s.CheckTradeData{
TradeNo: req.TradeNo,
Method: req.Method,
})
if err != nil {
slog.Debug(fmt.Sprintf("订单无需取消:%s", err.Error()))
} else {
err = s.Trade.CancelTrade(req.TradeNo, req.Method)
if err != nil {
slog.Warn("取消交易失败", "trade_no", req.TradeNo, "method", req.Method, "error", err)
}
}
return c.SendStatus(fiber.StatusNoContent)
}
func AlipayCallback(c *fiber.Ctx) (err error) {
// 解析请求
httpRequest := new(http.Request)
if err := fasthttpadaptor.ConvertRequest(c.Context(), httpRequest, false); err != nil {
return err
}
if err := httpRequest.ParseForm(); err != nil {
return err
}
notification, err := g.Alipay.DecodeNotification(httpRequest.Form)
if err != nil {
return err
}
slog.Debug("支付宝支付回调", "notification", fmt.Sprintf("%+v", notification))
// 查询交易信息
trade, err := q.Q.Trade.Where(q.Trade.InnerNo.Eq(notification.OutTradeNo)).Take()
if err != nil {
return c.SendString("success")
}
switch alipay.TradeStatus(notification.NotifyType) {
// 支付关闭
case alipay.TradeStatusClosed:
// todo 退款
// 非退款
switch trade2.Type(trade.Type) {
// 购买产品
case trade2.TypePurchase:
err = s.Resource.CancelResource(notification.OutTradeNo, time.Now(), true)
if err != nil {
return err
}
// 余额充值
case trade2.TypeRecharge:
err = s.User.RechargeCancel(notification.OutTradeNo, time.Now())
if err != nil {
return err
}
}
// 支付成功
case alipay.TradeStatusSuccess:
// 收集交易状态
payment, err := decimal.NewFromString(notification.TotalAmount)
if err != nil {
return err
}
paidAt, err := time.Parse("2006-01-02 15:04:05", notification.GmtPayment)
if err != nil {
return err
}
verified := &s.TradeSuccessResult{
TransId: notification.TradeNo,
Payment: payment,
Time: paidAt,
}
// todo 退款
// 非退款
switch trade2.Type(trade.Type) {
// 购买产品
case trade2.TypePurchase:
err = s.Resource.CompleteResource(notification.OutTradeNo, time.Now(), verified)
if err != nil {
return err
}
// 余额充值
case trade2.TypeRecharge:
err := s.User.RechargeConfirm(notification.OutTradeNo, verified)
if err != nil {
return err
}
}
}
return c.SendString("success")
}
func WechatPayCallback(c *fiber.Ctx) error {
// 解析请求参数
req := new(http.Request)
if err := fasthttpadaptor.ConvertRequest(c.Context(), req, false); err != nil {
return err
}
content := new(payments.Transaction)
_, err := g.WechatPay.Notify.ParseNotifyRequest(c.Context(), req, content)
if err != nil {
return err
}
slog.Debug("微信支付回调", "content", fmt.Sprintf("%+v", content))
// 查询交易信息
trade, err := q.Q.Trade.Where(q.Trade.InnerNo.Eq(*content.OutTradeNo)).Take()
if err != nil {
// 跳过测试通知
return nil
}
switch *content.TradeState {
// 支付成功
case "SUCCESS":
// 收集交易状态
payment := decimal.NewFromInt(*content.Amount.PayerTotal).Div(decimal.NewFromInt(100))
paidAt, err := time.Parse(time.RFC3339, *content.SuccessTime)
if err != nil {
return err
}
verified := &s.TradeSuccessResult{
TransId: *content.TransactionId,
Payment: payment,
Time: paidAt,
}
switch {
// 余额充值
case trade.Type == int32(trade2.TypeRecharge):
err := s.User.RechargeConfirm(*content.OutTradeNo, verified)
if err != nil {
return err
}
// 购买产品
case trade.Type == int32(trade2.TypePurchase):
err = s.Resource.CompleteResource(*content.OutTradeNo, time.Now(), verified)
if err != nil {
return err
}
}
}
return nil
}