Files
dujiao-next/internal/app/httpserver/middleware/callback_route_middleware.go
T

118 lines
3.5 KiB
Go

package middleware
import (
"net/http"
"strings"
settingsapp "github.com/dujiao-next/internal/modules/settings/application"
"github.com/dujiao-next/internal/constants"
paymenttransport "github.com/dujiao-next/internal/modules/payment/transport/http"
paymentcallbacktransport "github.com/dujiao-next/internal/modules/payment/transport/http/callback"
upstreamtransport "github.com/dujiao-next/internal/modules/upstreamapi/transport/http"
"github.com/gin-gonic/gin"
)
// defaultCallbackPaths 默认回调路径集合,用于在配置自定义路由后屏蔽默认路径
var defaultCallbackPaths = map[string]bool{
constants.DefaultPaymentCallbackPath: true,
constants.DefaultDujiaoPayWebhookPath: true,
constants.DefaultPaypalWebhookPath: true,
constants.DefaultStripeWebhookPath: true,
constants.DefaultUpstreamCallbackPath: true,
}
// CallbackRouteMiddleware 动态回调路由中间件。
// 当管理员配置了自定义回调路由时:
// - 匹配自定义路径 → 分发到对应 handler
// - 匹配默认回调路径 → 返回 404(隐藏默认路径)
// - 未配置自定义路由 → 放行,默认路由正常工作
func CallbackRouteMiddleware(
settingService *settingsapp.Service,
callbackHandler *paymentcallbacktransport.Handler,
webhookHandler *paymenttransport.WebhookHandler,
upstreamHandler *upstreamtransport.Handler,
) gin.HandlerFunc {
return func(c *gin.Context) {
path := strings.TrimRight(c.Request.URL.Path, "/")
method := c.Request.Method
// 快速过滤:只处理以 /api/ 开头的请求
if !strings.HasPrefix(path, "/api/") {
c.Next()
return
}
// 快速过滤:回调只用 POST 和 GET
if method != http.MethodPost && method != http.MethodGet {
c.Next()
return
}
routes := settingService.GetCallbackRoutesCached()
if routes == nil {
// 未配置自定义路由,放行使用默认路由
c.Next()
return
}
// 匹配自定义回调路径
switch path {
case routes.PaymentCallback:
if routes.PaymentCallback != "" && (method == http.MethodPost || method == http.MethodGet) {
callbackHandler.PaymentCallback(c)
c.Abort()
return
}
case routes.DujiaoPayWebhook:
if routes.DujiaoPayWebhook != "" && method == http.MethodPost && webhookHandler != nil {
webhookHandler.DujiaoPayWebhook(c)
c.Abort()
return
}
case routes.PaypalWebhook:
if routes.PaypalWebhook != "" && method == http.MethodPost && webhookHandler != nil {
webhookHandler.PaypalWebhook(c)
c.Abort()
return
}
case routes.StripeWebhook:
if routes.StripeWebhook != "" && method == http.MethodPost && webhookHandler != nil {
webhookHandler.StripeWebhook(c)
c.Abort()
return
}
case routes.UpstreamCallback:
if routes.UpstreamCallback != "" && method == http.MethodPost {
upstreamHandler.HandleCallback(c)
c.Abort()
return
}
}
// 屏蔽默认回调路径(当对应的自定义路由已配置时)
if defaultCallbackPaths[path] {
shouldBlock := false
switch path {
case constants.DefaultPaymentCallbackPath:
shouldBlock = routes.PaymentCallback != ""
case constants.DefaultDujiaoPayWebhookPath:
shouldBlock = routes.DujiaoPayWebhook != ""
case constants.DefaultPaypalWebhookPath:
shouldBlock = routes.PaypalWebhook != ""
case constants.DefaultStripeWebhookPath:
shouldBlock = routes.StripeWebhook != ""
case constants.DefaultUpstreamCallbackPath:
shouldBlock = routes.UpstreamCallback != ""
}
if shouldBlock {
c.AbortWithStatus(http.StatusNotFound)
return
}
}
c.Next()
}
}