package callback import ( "context" "fmt" "net/http" "strings" "github.com/gofiber/fiber/v2" "github.com/valyala/fasthttp/fasthttpadaptor" "go.uber.org/zap" "github.com/break/junhong_cmp_fiber/internal/model" orderService "github.com/break/junhong_cmp_fiber/internal/service/order" rechargeService "github.com/break/junhong_cmp_fiber/internal/service/recharge" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/fuiou" "github.com/break/junhong_cmp_fiber/pkg/response" "github.com/break/junhong_cmp_fiber/pkg/wechat" ) // AgentRechargeServiceInterface 代理充值服务接口 type AgentRechargeServiceInterface interface { HandlePaymentCallback(ctx context.Context, rechargeNo string, paymentMethod string, paymentTransactionID string) error } // WechatConfigServiceInterface 支付配置服务接口 type WechatConfigServiceInterface interface { GetActiveConfig(ctx context.Context) (*model.WechatConfig, error) } type PaymentHandler struct { orderService *orderService.Service rechargeService *rechargeService.Service agentRechargeService AgentRechargeServiceInterface wechatPayment wechat.PaymentServiceInterface wechatConfigService WechatConfigServiceInterface logger *zap.Logger } func NewPaymentHandler( orderService *orderService.Service, rechargeService *rechargeService.Service, agentRechargeService AgentRechargeServiceInterface, wechatPayment wechat.PaymentServiceInterface, wechatConfigService WechatConfigServiceInterface, logger *zap.Logger, ) *PaymentHandler { return &PaymentHandler{ orderService: orderService, rechargeService: rechargeService, agentRechargeService: agentRechargeService, wechatPayment: wechatPayment, wechatConfigService: wechatConfigService, logger: logger, } } // WechatPayCallback 微信支付回调(带签名验证) // POST /api/callback/wechat-pay func (h *PaymentHandler) WechatPayCallback(c *fiber.Ctx) error { if h.wechatPayment == nil { return errors.New(errors.CodeWechatCallbackInvalid, "微信支付服务未配置") } var httpReq http.Request fasthttpadaptor.ConvertRequest(c.Context(), &httpReq, true) ctx := context.Background() _, err := h.wechatPayment.HandlePaymentNotify(&httpReq, func(result *wechat.PaymentNotifyResult) error { if result.TradeState != "SUCCESS" { return nil } // TODO: 按 payment_config_id 加载配置验签(当前留桩,仍用 wechatPayment 单例验签) // 按订单号前缀分发 outTradeNo := result.OutTradeNo switch { case strings.HasPrefix(outTradeNo, "ORD"): return h.orderService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentMethodWechat) case strings.HasPrefix(outTradeNo, constants.AssetRechargeOrderPrefix): return h.rechargeService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentMethodWechat, result.TransactionID) case strings.HasPrefix(outTradeNo, constants.AgentRechargeOrderPrefix): if h.agentRechargeService != nil { return h.agentRechargeService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentMethodWechat, result.TransactionID) } return fmt.Errorf("代理充值服务未配置,无法处理订单: %s", outTradeNo) default: return fmt.Errorf("未知订单号前缀: %s", outTradeNo) } }) if err != nil { return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "处理微信支付回调失败") } return response.Success(c, map[string]string{"return_code": "SUCCESS"}) } type AlipayCallbackRequest struct { OrderNo string `json:"out_trade_no" form:"out_trade_no"` } // AlipayCallback 支付宝回调 // POST /api/callback/alipay func (h *PaymentHandler) AlipayCallback(c *fiber.Ctx) error { var req AlipayCallbackRequest if err := c.BodyParser(&req); err != nil { return errors.New(errors.CodeInvalidParam, "请求参数解析失败") } if req.OrderNo == "" { return errors.New(errors.CodeInvalidParam, "订单号不能为空") } ctx := c.UserContext() // 按订单号前缀分发 switch { case strings.HasPrefix(req.OrderNo, "ORD"): if err := h.orderService.HandlePaymentCallback(ctx, req.OrderNo, model.PaymentMethodAlipay); err != nil { return err } case strings.HasPrefix(req.OrderNo, constants.AssetRechargeOrderPrefix): if err := h.rechargeService.HandlePaymentCallback(ctx, req.OrderNo, model.PaymentMethodAlipay, ""); err != nil { return err } case strings.HasPrefix(req.OrderNo, constants.AgentRechargeOrderPrefix): if h.agentRechargeService != nil { if err := h.agentRechargeService.HandlePaymentCallback(ctx, req.OrderNo, model.PaymentMethodAlipay, ""); err != nil { return err } } default: return errors.New(errors.CodeInvalidParam, "未知订单号前缀") } return c.SendString("success") } // FuiouPayCallback 富友支付回调(带签名验证) // POST /api/callback/fuiou-pay func (h *PaymentHandler) FuiouPayCallback(c *fiber.Ctx) error { body := c.Body() if len(body) == 0 { return errors.New(errors.CodeFuiouCallbackInvalid, "回调请求体为空") } ctx := c.UserContext() c.Set("Content-Type", "text/xml; charset=gbk") activeConfig, err := h.wechatConfigService.GetActiveConfig(ctx) if err != nil || activeConfig == nil { h.logger.Error("富友回调:加载支付配置失败", zap.Error(err)) return c.Send(fuiou.BuildNotifyFailResponse("payment config unavailable")) } client, err := fuiou.NewClient( activeConfig.FyInsCd, activeConfig.FyMchntCd, activeConfig.FyTermID, activeConfig.FyAPIURL, activeConfig.FyNotifyURL, activeConfig.FyPrivateKey, activeConfig.FyPublicKey, h.logger, ) if err != nil { h.logger.Error("富友回调:构造客户端失败", zap.Error(err)) return c.Send(fuiou.BuildNotifyFailResponse("client init failed")) } notify, err := client.VerifyNotify(body) if err != nil { if notify != nil { h.logger.Warn("富友回调:非成功结果", zap.String("result_code", notify.ResultCode), zap.String("result_msg", notify.ResultMsg)) return c.Send(fuiou.BuildNotifySuccessResponse()) } h.logger.Error("富友回调:验签或解析失败", zap.Error(err)) return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } orderNo := notify.MchntOrderNo switch { case strings.HasPrefix(orderNo, "ORD"): if err := h.orderService.HandlePaymentCallback(ctx, orderNo, "fuiou"); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } case strings.HasPrefix(orderNo, constants.AssetRechargeOrderPrefix): if err := h.rechargeService.HandlePaymentCallback(ctx, orderNo, "fuiou", notify.TransactionId); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } case strings.HasPrefix(orderNo, constants.AgentRechargeOrderPrefix): if h.agentRechargeService != nil { if err := h.agentRechargeService.HandlePaymentCallback(ctx, orderNo, "fuiou", notify.TransactionId); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } } default: return c.Send(fuiou.BuildNotifyFailResponse("unknown order prefix")) } return c.Send(fuiou.BuildNotifySuccessResponse()) }