完善支付流程,实现异步事件

This commit is contained in:
2026-07-10 17:11:39 +08:00
parent 5d00111887
commit d961538027
6 changed files with 143 additions and 6 deletions

View File

@@ -59,3 +59,4 @@ SFTPAY_ROUTE_ID=
SFTPAY_APP_PRIVATE_KEY=
SFTPAY_PUBLIC_KEY=
SFTPAY_RETURN_URL=
SFTPAY_NOTIFY_URL=

View File

@@ -233,7 +233,7 @@ type QueryTradeResp struct {
RefundState int32 `json:"refundState"`
DrType *string `json:"drType"`
ExtParam *string `json:"extParam"`
PayTime *int64 `json:"payTime"`
PayTime *string `json:"payTime"`
Subject string `json:"subject"`
TradeFee *int64 `json:"tradeFee"`
CashFee *int64 `json:"cashFee"`
@@ -443,3 +443,11 @@ const (
SftRefundIng SftTradeState = "REFUND_ING"
SftTradeClosed SftTradeState = "TRADE_CLOSED"
)
type SftNotifyType string
var (
SftNotifyTypeTrade = SftNotifyType("sft.trade.notify")
SftNotifyTypeRefund = SftNotifyType("sft.refund.notify")
SftNotifyTypeDivision = SftNotifyType("sft.division.notify")
)

View File

@@ -1,6 +1,7 @@
package handlers
import (
"encoding/json"
"log/slog"
"platform/web/auth"
"platform/web/core"
@@ -11,6 +12,7 @@ import (
"time"
"github.com/gofiber/fiber/v2"
"github.com/shopspring/decimal"
)
// PageTradeByAdmin 分页查询所有订单
@@ -429,3 +431,99 @@ func TradeCheck(c *fiber.Ctx) error {
return c.JSON(result)
}
// ============================================================
// 交易回调
func TradeCallbackSft(c *fiber.Ctx) error {
// 解析请求参数
var req TradeCallbackSftReq
if err := g.Validator.ParseBody(c, &req); err != nil {
return core.NewServErr("解析请求失败", err)
}
var bizData TradeCallbackSftReqBizData
if err := json.Unmarshal([]byte(*req.BizData), &bizData); err != nil {
return core.NewServErr("解析业务数据失败", err)
}
// 检查通知
if bizData.MsgType != string(g.SftNotifyTypeTrade) {
return core.NewBizErr("消息类型处理异常")
}
if bizData.State != string(g.SftTradeSuccess) {
return core.NewBizErr("订单状态处理异常")
}
// 更新订单状态
if bizData.PayType == nil {
return core.NewBizErr("支付渠道解析错误")
}
var acquirer m.TradeAcquirer
switch *bizData.PayType {
case string(g.SftAlipay):
acquirer = m.TradeAcquirerAlipay
case string(g.SftWeChat):
acquirer = m.TradeAcquirerWechat
case string(g.SftUnionPay):
acquirer = m.TradeAcquirerUnionPay
default:
return core.NewBizErr("支付渠道类型异常")
}
var time, err = time.Parse("2006-01-02 15:04:05", *bizData.PayTime)
if err != nil {
return core.NewBizErr("时间解析错误")
}
err = s.Trade.CompleteTradeByNotify(bizData.MchOrderNo, bizData.PayOrderId, &s.TradeSuccessResult{
Acquirer: acquirer,
Actual: decimal.NewFromInt(bizData.Amount).Div(decimal.NewFromInt(100)),
Time: time,
})
if err != nil {
return core.NewServErr("完成交易失败", err)
}
return c.Send([]byte("SUCCESS"))
}
type TradeCallbackSftReq struct {
Code string `json:"code"`
Msg *string `json:"msg"`
Sign *string `json:"sign"`
BizData *string `json:"bizData"`
SignType *string `json:"signType"`
Timestamp string `json:"timestamp"`
}
type TradeCallbackSftReqBizData struct {
MsgType string `json:"msgType"`
PayOrderId string `json:"payOrderId"`
State string `json:"state"`
MercNo string `json:"mercNo"`
StoreId string `json:"storeId"`
MchOrderNo string `json:"mchOrderNo"`
IfCode string `json:"ifCode"`
Amount int64 `json:"amount"`
Currency string `json:"currency"`
Subject string `json:"subject"`
Body string `json:"body"`
ChannelTradeNo *string `json:"channelTradeNo"`
ChannelSendNo *string `json:"channelSendNo"`
UserId *string `json:"userId"`
ExtParam *string `json:"extParam"`
PayTime *string `json:"payTime"`
Note *string `json:"note"`
PayType *string `json:"payType"`
DrType *string `json:"drType"`
TradeFee *int64 `json:"tradeFee"`
CashFee *int64 `json:"cashFee"`
RefundAmt *int64 `json:"refundAmt"`
RefundState *int32 `json:"refundState"`
SettlementType *string `json:"settlementType"`
}

View File

@@ -30,6 +30,7 @@ func ApplyMiddlewares(app *fiber.App) {
// logger
app.Use(logger.New(logger.Config{
TimeFormat: "2006-01-02 15:04:05",
Next: func(c *fiber.Ctx) bool {
return c.Path() == "/favicon.ico"
},

View File

@@ -23,6 +23,7 @@ func ApplyRouters(app *fiber.App) {
// 回调
callbacks := app.Group("/callback")
callbacks.Get("/identify", handlers.IdentifyCallbackNew)
callbacks.All("/trade/sft", handlers.TradeCallbackSft)
// 临时
if env.RunMode == env.RunModeDev {

View File

@@ -146,6 +146,7 @@ func (s *tradeService) Create(user *m.User, tradeData *CreateTradeData, productD
Currency: "cny",
ClientIp: "123.52.74.23",
OrderTimeout: u.P(expireAt.Format("2006-01-02 15:04:05")),
NotifyUrl: &env.SftNotifyUrl,
})
if err != nil {
return nil, err
@@ -238,23 +239,47 @@ func (s *tradeService) CompleteTrade(user *m.User, ref *TradeRef) error {
}
}
// 获取交易信息
trade, err := q.Trade.
Preload(q.Trade.User).
Where(q.Trade.InnerNo.Eq(ref.TradeNo)).
Take()
if err != nil {
return core.NewBizErr("获取交易信息失败", err)
}
if trade.User == nil || trade.User.ID != user.ID {
return core.NewBizErr("交易信息不存在")
}
// 更新交易状态
err = s.OnCompleteTrade(user, ref.TradeNo, result.TransId, &result.Success)
err = s.OnCompleteTrade(trade, ref.TradeNo, result.TransId, &result.Success)
if err != nil {
return core.NewServErr("处理交易失败", err)
}
return nil
}
func (s *tradeService) OnCompleteTrade(user *m.User, interNo string, outerNo string, result *TradeSuccessResult) error {
// 获取交易信息
func (s *tradeService) CompleteTradeByNotify(interNo string, outerNo string, result *TradeSuccessResult) error {
trade, err := q.Trade.
Preload(q.Trade.User).
Where(q.Trade.InnerNo.Eq(interNo)).
Take()
if err != nil {
return core.NewBizErr("获取交易信息失败", err)
}
if trade.User == nil {
return core.NewBizErr("交易信息不存在")
}
err = s.OnCompleteTrade(trade, interNo, outerNo, result)
if err != nil {
return core.NewServErr("处理交易失败", err)
}
return nil
}
func (s *tradeService) OnCompleteTrade(trade *m.Trade, interNo string, outerNo string, result *TradeSuccessResult) error {
user := trade.User
// 检查交易状态
switch trade.Status {
@@ -646,7 +671,10 @@ func (s *tradeService) CheckTrade(ref *TradeRef) (*CheckTradeResult, error) {
result.Success.Acquirer = m.TradeAcquirerUnionPay
}
result.Success.Actual = decimal.NewFromInt(resp.Amount).Div(decimal.NewFromInt(100))
result.Success.Time = time.UnixMilli(*resp.PayTime)
result.Success.Time, err = time.Parse("2006-01-02 15:04:05", *resp.PayTime)
if err != nil {
return nil, err
}
}
// 不支持的支付方式