package callback import ( "context" "net/http" "net/url" "strconv" "strings" "time" "github.com/gofiber/fiber/v2" "github.com/valyala/fasthttp/fasthttpadaptor" "go.uber.org/zap" agentrechargeApp "github.com/break/junhong_cmp_fiber/internal/application/agentrecharge" "github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog" "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" rechargeOrderSvc "github.com/break/junhong_cmp_fiber/internal/service/recharge_order" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/alipay" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/fuiou" pkgmiddleware "github.com/break/junhong_cmp_fiber/pkg/middleware" "github.com/break/junhong_cmp_fiber/pkg/wechat" "gorm.io/gorm" ) // AgentRechargeServiceInterface 代理充值服务接口 type AgentRechargeServiceInterface interface { HandlePaymentCallback(ctx context.Context, rechargeNo string, paymentMethod string, paymentTransactionID string, paidAmount int64) error } // WechatConfigServiceInterface 支付配置服务接口 type WechatConfigServiceInterface interface { GetActiveConfig(ctx context.Context) (*model.WechatConfig, error) GetConfigForCallback(ctx context.Context, orderNo string) (*model.WechatConfig, error) } type PaymentHandler struct { orderService *orderService.Service rechargeService *rechargeService.Service rechargeOrderService *rechargeOrderSvc.Service agentRechargeService AgentRechargeServiceInterface wechatPayment wechat.PaymentServiceInterface wechatConfigService WechatConfigServiceInterface paymentStore *postgres.PaymentStore agentPaymentConfirm *agentrechargeApp.ConfirmOnlinePaymentService integration *integrationlog.Repository logger *zap.Logger } func NewPaymentHandler( orderService *orderService.Service, rechargeService *rechargeService.Service, rechargeOrderService *rechargeOrderSvc.Service, agentRechargeService AgentRechargeServiceInterface, wechatPayment wechat.PaymentServiceInterface, wechatConfigService WechatConfigServiceInterface, paymentStore *postgres.PaymentStore, agentPaymentConfirm *agentrechargeApp.ConfirmOnlinePaymentService, integration *integrationlog.Repository, logger *zap.Logger, ) *PaymentHandler { return &PaymentHandler{ orderService: orderService, rechargeService: rechargeService, rechargeOrderService: rechargeOrderService, agentRechargeService: agentRechargeService, wechatPayment: wechatPayment, wechatConfigService: wechatConfigService, paymentStore: paymentStore, agentPaymentConfirm: agentPaymentConfirm, integration: integration, logger: logger, } } type verifiedPaymentCallback struct { PaymentNo string PaymentMethod string TransactionID string Amount int64 ConfigID uint MerchantIdentity string PaidAt time.Time Provider string RawPayload []byte ContentType string } // WechatPayCallback 微信支付回调(带签名验证) // POST /api/callback/wechat-pay func (h *PaymentHandler) WechatPayCallback(c *fiber.Ctx) error { body := c.Body() ctx := c.UserContext() // 预解析订单号(不验签),用于按 payment_config_id 加载创建订单时所用的配置 orderNo, err := wechat.PeekOrderNo(body) if err != nil { h.logger.Error("微信回调:预解析订单号失败", zap.Error(err)) return errors.New(errors.CodeWechatCallbackInvalid, "回调数据格式错误") } cfg, err := h.wechatConfigService.GetConfigForCallback(ctx, orderNo) if err != nil || cfg == nil { h.logger.Error("微信回调:加载支付配置失败", zap.String("order_no", orderNo), zap.Error(err), ) return errors.New(errors.CodeWechatCallbackInvalid, "微信支付服务未配置") } switch cfg.ProviderType { case model.ProviderTypeWechatV2: paymentSvc := wechat.NewPaymentV2Service(cfg.OaAppID, cfg.WxMchID, cfg.WxAPIV2Key, cfg.WxNotifyURL, h.logger) result, err := paymentSvc.VerifyCallback(body) if err != nil { return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "微信 v2 回调验签失败") } if result.TradeState != "SUCCESS" { return h.wechatV2SuccessResponse(c) } // TotalFee 为字符串格式的分,解析失败则降级为 0(后续 handlePaymentCallback 会记录日志) totalFee, _ := strconv.ParseInt(result.TotalFee, 10, 64) paidAt, _ := time.ParseInLocation("20060102150405", result.SuccessTime, time.Local) if err := h.dispatchWechatCallback(ctx, verifiedPaymentCallback{ PaymentNo: result.OutTradeNo, PaymentMethod: model.PaymentMethodWechat, TransactionID: result.TransactionID, Amount: totalFee, ConfigID: cfg.ID, MerchantIdentity: cfg.WxMchID, PaidAt: paidAt, Provider: constants.IntegrationProviderWechatPay, RawPayload: body, ContentType: c.Get("Content-Type"), }); err != nil { return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "处理微信支付回调失败") } case model.ProviderTypeWechat: if h.wechatPayment == nil { return errors.New(errors.CodeWechatCallbackInvalid, "微信 v3 支付未初始化") } var httpReq http.Request fasthttpadaptor.ConvertRequest(c.Context(), &httpReq, true) _, err := h.wechatPayment.HandlePaymentNotify(&httpReq, func(result *wechat.PaymentNotifyResult) error { if result.TradeState != "SUCCESS" { return nil } paidAt, _ := time.Parse(time.RFC3339, result.SuccessTime) return h.dispatchWechatCallback(ctx, verifiedPaymentCallback{ PaymentNo: result.OutTradeNo, PaymentMethod: model.PaymentMethodWechat, TransactionID: result.TransactionID, Amount: result.TotalAmount, ConfigID: cfg.ID, MerchantIdentity: cfg.WxMchID, PaidAt: paidAt, Provider: constants.IntegrationProviderWechatPay, RawPayload: body, ContentType: c.Get("Content-Type"), }) }) if err != nil { return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "处理微信支付回调失败") } default: return errors.New(errors.CodeWechatCallbackInvalid, "不支持的支付渠道类型") } return h.wechatV2SuccessResponse(c) } func (h *PaymentHandler) dispatchPaymentRecordCallback(ctx context.Context, callback verifiedPaymentCallback) (bool, error) { if h.paymentStore != nil { payment, err := h.paymentStore.GetByPaymentNo(ctx, callback.PaymentNo) if err == nil { switch payment.OrderType { case model.PaymentOrderTypePackage: return true, h.orderService.HandlePaymentRecordCallback(ctx, callback.PaymentNo, callback.PaymentMethod, callback.TransactionID, callback.Amount) case model.PaymentOrderTypeRecharge: if h.rechargeOrderService != nil { return true, h.rechargeOrderService.HandlePaymentCallback(ctx, callback.PaymentNo, callback.PaymentMethod, callback.TransactionID) } return true, errors.New(errors.CodeInternalError, "充值订单服务未配置") case model.PaymentOrderTypeAgentRecharge: return true, h.confirmAgentRechargePayment(ctx, callback) default: return true, errors.New(errors.CodeInvalidStatus, "未知支付记录类型") } } if err != gorm.ErrRecordNotFound { return true, errors.Wrap(errors.CodeDatabaseError, err, "查询支付记录失败") } } return false, nil } // dispatchWechatCallback 优先按支付单分发,旧单号继续按前缀兼容处理。 func (h *PaymentHandler) dispatchWechatCallback(ctx context.Context, callback verifiedPaymentCallback) error { handled, err := h.dispatchPaymentRecordCallback(ctx, callback) if handled || err != nil { return err } switch { case strings.HasPrefix(callback.PaymentNo, "ORD"): return h.orderService.HandlePaymentCallback(ctx, callback.PaymentNo, model.PaymentMethodWechat, callback.Amount) case strings.HasPrefix(callback.PaymentNo, constants.AssetRechargeOrderPrefix): if h.rechargeOrderService != nil { return h.rechargeOrderService.HandlePaymentCallback(ctx, callback.PaymentNo, model.PaymentByWechat, callback.TransactionID) } return errors.New(errors.CodeInternalError, "充值订单服务未配置") case strings.HasPrefix(callback.PaymentNo, constants.AgentRechargeOrderPrefix): if h.agentRechargeService != nil { return h.agentRechargeService.HandlePaymentCallback(ctx, callback.PaymentNo, model.PaymentMethodWechat, callback.TransactionID, callback.Amount) } return errors.New(errors.CodeInternalError, "代理充值服务未配置") default: return errors.New(errors.CodeInvalidStatus, "未知订单号前缀") } } func (h *PaymentHandler) confirmAgentRechargePayment(ctx context.Context, callback verifiedPaymentCallback) error { if h.agentPaymentConfirm == nil || h.integration == nil { return errors.New(errors.CodeInternalError, "代理充值支付回调能力未配置") } resourceID, correlationID := callback.PaymentNo, callback.PaymentNo idempotencyKey := callback.TransactionID if idempotencyKey == "" { idempotencyKey = callback.PaymentNo } log, _, err := h.integration.RecordInbound(ctx, integrationlog.InboundAttempt{ IdempotencyKey: idempotencyKey, Provider: callback.Provider, Operation: constants.IntegrationOperationPaymentCallback, ExternalID: callback.TransactionID, ResourceType: constants.IntegrationResourceTypeAgentRechargePayment, ResourceID: &resourceID, RawPayload: callback.RawPayload, ContentType: callback.ContentType, RequestID: pkgmiddleware.GetRequestIDFromContext(ctx), CorrelationID: &correlationID, }) if err != nil { return err } result, confirmErr := h.agentPaymentConfirm.Execute(ctx, agentrechargeApp.ConfirmOnlinePaymentCommand{ PaymentNo: callback.PaymentNo, PaymentMethod: callback.PaymentMethod, ConfigID: callback.ConfigID, MerchantIdentity: callback.MerchantIdentity, ThirdPartyTradeNo: callback.TransactionID, Amount: callback.Amount, PaidAt: callback.PaidAt, CorrelationID: correlationID, }) if confirmErr != nil { h.completePaymentCallbackLog(ctx, log, integrationlog.Completion{ Result: constants.IntegrationResultFailed, ProviderMessage: confirmErr.Error(), ResponseSummary: map[string]any{"confirmed": false}, }) return confirmErr } if log.Result == constants.IntegrationResultPending { _, err = h.integration.Complete(ctx, log.IntegrationID, integrationlog.Completion{ Result: constants.IntegrationResultSuccess, ProviderCode: "SUCCESS", ResponseSummary: map[string]any{"confirmed": true, "already_confirmed": result.AlreadyConfirmed}, StateChanged: !result.AlreadyConfirmed, }) if err != nil { return err } } return nil } func (h *PaymentHandler) completePaymentCallbackLog(ctx context.Context, log *model.IntegrationLog, completion integrationlog.Completion) { if log == nil || log.Result != constants.IntegrationResultPending { return } if _, err := h.integration.Complete(ctx, log.IntegrationID, completion); err != nil { h.logger.Error("支付回调 Integration Log 终结失败", zap.String("integration_id", log.IntegrationID), zap.Error(err), ) } } // wechatV2SuccessResponse 返回微信 v2 要求的 XML 格式成功响应 // v2 API 要求 XML 格式的 return_code=SUCCESS,否则微信会持续重试回调 func (h *PaymentHandler) wechatV2SuccessResponse(c *fiber.Ctx) error { c.Set("Content-Type", "text/xml; charset=utf-8") return c.SendString(``) } // AlipayCallback 支付宝异步回调(带验签与多维度安全校验) // POST /api/callback/alipay func (h *PaymentHandler) AlipayCallback(c *fiber.Ctx) error { ctx := c.UserContext() // 从原始请求体解析 form 参数(x-www-form-urlencoded) values, err := url.ParseQuery(string(c.Body())) if err != nil { h.logger.Error("支付宝回调:解析 form 参数失败", zap.Error(err)) return errors.New(errors.CodeInvalidParam, "回调数据格式错误") } // 未验签前只读取 out_trade_no,用于回溯创建支付单时所用的配置 outTradeNo := values.Get("out_trade_no") if outTradeNo == "" { h.logger.Error("支付宝回调:out_trade_no 为空") return errors.New(errors.CodeInvalidParam, "订单号不能为空") } // 按 payment_config_id 加载创建支付单时所用的配置(支持已停用配置) cfg, err := h.wechatConfigService.GetConfigForCallback(ctx, outTradeNo) if err != nil || cfg == nil { h.logger.Error("支付宝回调:加载支付配置失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付配置不可用") } // 使用支付宝公钥验签(DecodeNotification 内部完成签名校验) notification, err := alipay.DecodeNotification(ctx, cfg, values) if err != nil { h.logger.Error("支付宝回调:验签失败", zap.String("out_trade_no", outTradeNo), zap.Uint("config_id", cfg.ID), zap.Error(err), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付宝回调验签失败") } // 校验 app_id 与配置一致,防止跨商户混用 if notification.AppId != cfg.AliAppID { h.logger.Error("支付宝回调:app_id 不匹配", zap.String("out_trade_no", outTradeNo), zap.String("notify_app_id", notification.AppId), zap.String("config_app_id", cfg.AliAppID), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付宝 app_id 校验失败") } // 非终态交易(如 WAIT_BUYER_PAY、TRADE_CLOSED)直接返回 success,不推进业务 tradeStatus := string(notification.TradeStatus) if tradeStatus != "TRADE_SUCCESS" && tradeStatus != "TRADE_FINISHED" { h.logger.Info("支付宝回调:非成功交易状态,忽略", zap.String("out_trade_no", outTradeNo), zap.String("trade_status", tradeStatus), ) return c.SendString("success") } // 查询支付记录 payment, err := h.paymentStore.GetByPaymentNo(ctx, outTradeNo) if err != nil { if err == gorm.ErrRecordNotFound { h.logger.Error("支付宝回调:支付记录不存在", zap.String("out_trade_no", outTradeNo), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付记录不存在") } h.logger.Error("支付宝回调:查询支付记录失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return errors.Wrap(errors.CodeDatabaseError, err, "查询支付记录失败") } // 校验支付方式必须为 alipay,防止其他通道的 payment_no 被误用 if payment.PaymentMethod != model.PaymentByAlipay { h.logger.Error("支付宝回调:支付方式不匹配", zap.String("out_trade_no", outTradeNo), zap.String("payment_method", payment.PaymentMethod), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付通道校验失败") } // 校验 payment_config_id 与当前配置一致;旧数据为空时兼容并记录 warn if payment.PaymentConfigID == nil { h.logger.Warn("支付宝回调:payment_config_id 为空,跳过配置 ID 校验(旧数据兼容)", zap.String("out_trade_no", outTradeNo), zap.Uint("config_id", cfg.ID), ) } else if *payment.PaymentConfigID != cfg.ID { h.logger.Error("支付宝回调:payment_config_id 不匹配", zap.String("out_trade_no", outTradeNo), zap.Uint("payment_config_id", *payment.PaymentConfigID), zap.Uint("callback_config_id", cfg.ID), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付配置 ID 校验失败") } // 校验金额:使用 total_amount(原始订单金额)而非 buyer_pay_amount, // 避免优惠券/积分场景导致内部订单金额被错误记低 notifyAmountFen, err := alipay.YuanToFen(notification.TotalAmount) if err != nil { h.logger.Error("支付宝回调:total_amount 转换失败", zap.String("out_trade_no", outTradeNo), zap.String("total_amount", notification.TotalAmount), zap.Error(err), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付金额格式错误") } if notifyAmountFen != payment.Amount { h.logger.Error("支付宝回调:金额不一致", zap.String("out_trade_no", outTradeNo), zap.Int64("notify_amount_fen", notifyAmountFen), zap.Int64("payment_amount_fen", payment.Amount), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付金额校验失败") } // 新代理充值由统一确认事务原子写入交易号,旧业务保持原处理方式。 if payment.OrderType != model.PaymentOrderTypeAgentRecharge { if err := h.paymentStore.UpdatePaymentInfo(ctx, payment.ID, notification.TradeNo, nil); err != nil { h.logger.Error("支付宝回调:写入 third_party_trade_no 失败", zap.String("out_trade_no", outTradeNo), zap.String("trade_no", notification.TradeNo), zap.Error(err), ) // 不中断存量业务,继续由原幂等业务层处理状态。 } } // 按支付单 order_type 分发业务 switch payment.OrderType { case model.PaymentOrderTypePackage: if err := h.orderService.HandlePaymentRecordCallback(ctx, outTradeNo, model.PaymentByAlipay, notification.TradeNo, payment.Amount); err != nil { h.logger.Error("支付宝回调:推进套餐订单失败", zap.String("out_trade_no", outTradeNo), zap.String("trade_no", notification.TradeNo), zap.Error(err), ) return errors.Wrap(errors.CodeInternalError, err, "处理支付宝支付回调失败") } h.logger.Info("支付宝回调:套餐订单支付成功", zap.String("out_trade_no", outTradeNo), zap.String("trade_no", notification.TradeNo), zap.String("order_type", payment.OrderType), ) case model.PaymentOrderTypeRecharge: if h.rechargeOrderService == nil { h.logger.Error("支付宝回调:充值订单服务未配置", zap.String("out_trade_no", outTradeNo), ) return errors.New(errors.CodeInternalError, "充值订单服务未配置") } if err := h.rechargeOrderService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentByAlipay, notification.TradeNo); err != nil { h.logger.Error("支付宝回调:推进充值订单失败", zap.String("out_trade_no", outTradeNo), zap.String("trade_no", notification.TradeNo), zap.Error(err), ) return errors.Wrap(errors.CodeInternalError, err, "处理支付宝充值回调失败") } h.logger.Info("支付宝回调:充值订单支付成功", zap.String("out_trade_no", outTradeNo), zap.String("trade_no", notification.TradeNo), zap.String("order_type", payment.OrderType), ) case model.PaymentOrderTypeAgentRecharge: paidAt, parseErr := time.ParseInLocation("2006-01-02 15:04:05", notification.GmtPayment, time.Local) if parseErr != nil { h.logger.Error("支付宝回调:付款时间格式无效", zap.String("out_trade_no", outTradeNo), zap.Error(parseErr), ) return errors.New(errors.CodeWechatCallbackInvalid, "支付宝付款时间格式错误") } if err := h.confirmAgentRechargePayment(ctx, verifiedPaymentCallback{ PaymentNo: outTradeNo, PaymentMethod: model.PaymentByAlipay, TransactionID: notification.TradeNo, Amount: notifyAmountFen, ConfigID: cfg.ID, MerchantIdentity: notification.AppId, PaidAt: paidAt, Provider: constants.IntegrationProviderAlipay, RawPayload: c.Body(), ContentType: c.Get("Content-Type"), }); err != nil { h.logger.Error("支付宝回调:确认代理充值支付失败", zap.String("out_trade_no", outTradeNo), zap.Error(err), ) return errors.Wrap(errors.CodeInternalError, err, "处理支付宝代理充值回调失败") } default: h.logger.Error("支付宝回调:未知支付记录类型", zap.String("out_trade_no", outTradeNo), zap.String("order_type", payment.OrderType), ) return errors.New(errors.CodeInternalError, "未知支付记录类型") } return c.SendString("success") } // FuiouPayCallback 富友支付回调(带签名验证) // POST /api/callback/fuiou-pay func (h *PaymentHandler) FuiouPayCallback(c *fiber.Ctx) error { body, bodySource := fuiouCallbackPayload(c) if strings.TrimSpace(string(body)) == "" { return errors.New(errors.CodeFuiouCallbackInvalid, "回调请求体为空") } ctx := c.UserContext() c.Set("Content-Type", "text/plain; charset=utf-8") // 预解析订单号(不验签),用于按 payment_config_id 加载创建订单时所用的配置 preNotify, err := fuiou.ParseNotify(body) if err != nil { h.logger.Error("富友回调:预解析失败", zap.Error(err), zap.String("payload_source", bodySource), zap.Int("raw_body_len", len(c.Body())), zap.Int("payload_len", len(body)), zap.String("content_type", c.Get("Content-Type")), zap.String("query", string(c.Request().URI().QueryString())), ) return c.Send(fuiou.BuildNotifyFailResponse("parse failed")) } cfg, err := h.wechatConfigService.GetConfigForCallback(ctx, preNotify.MchntOrderNo) if err != nil || cfg == nil { h.logger.Error("富友回调:加载支付配置失败", zap.String("order_no", preNotify.MchntOrderNo), zap.Error(err), ) return c.Send(fuiou.BuildNotifyFailResponse("payment config unavailable")) } if cfg.ProviderType != model.ProviderTypeFuiou || strings.TrimSpace(preNotify.InsCd) != strings.TrimSpace(cfg.FyInsCd) || strings.TrimSpace(preNotify.MchntCd) != strings.TrimSpace(cfg.FyMchntCd) { h.logger.Error("富友回调:支付配置与回调商户不匹配", zap.String("order_no", preNotify.MchntOrderNo), zap.Uint("config_id", cfg.ID), zap.String("config_name", cfg.Name), zap.String("callback_ins_cd", preNotify.InsCd), zap.String("config_ins_cd", cfg.FyInsCd), zap.String("callback_mchnt_cd", preNotify.MchntCd), zap.String("config_mchnt_cd", cfg.FyMchntCd), zap.String("provider_type", cfg.ProviderType), ) return c.Send(fuiou.BuildNotifyFailResponse("payment config mismatch")) } client, err := fuiou.NewClient( cfg.FyInsCd, cfg.FyMchntCd, cfg.FyTermID, cfg.FyAPIURL, cfg.FyNotifyURL, cfg.FyPrivateKey, cfg.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), zap.String("order_no", preNotify.MchntOrderNo), zap.Uint("config_id", cfg.ID), zap.String("config_name", cfg.Name), zap.String("callback_ins_cd", preNotify.InsCd), zap.String("config_ins_cd", cfg.FyInsCd), zap.String("callback_mchnt_cd", preNotify.MchntCd), zap.String("config_mchnt_cd", cfg.FyMchntCd), ) return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } orderNo := notify.MchntOrderNo // OrderAmt 为字符串格式的分,解析失败则降级为 0 orderAmt, _ := strconv.ParseInt(notify.OrderAmt, 10, 64) if handled, err := h.dispatchPaymentRecordCallback(ctx, verifiedPaymentCallback{ PaymentNo: orderNo, PaymentMethod: "fuiou", TransactionID: notify.TransactionId, Amount: orderAmt, ConfigID: cfg.ID, MerchantIdentity: cfg.FyMchntCd, Provider: model.ProviderTypeFuiou, RawPayload: body, ContentType: c.Get("Content-Type"), }); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } else if handled { return c.Send(fuiou.BuildNotifySuccessResponse()) } switch { case strings.HasPrefix(orderNo, "ORD"): if err := h.orderService.HandlePaymentCallback(ctx, orderNo, "fuiou", orderAmt); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } case strings.HasPrefix(orderNo, constants.AssetRechargeOrderPrefix): if h.rechargeOrderService != nil { if err := h.rechargeOrderService.HandlePaymentCallback(ctx, orderNo, "fuiou", notify.TransactionId); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } return c.Send(fuiou.BuildNotifySuccessResponse()) } return c.Send(fuiou.BuildNotifyFailResponse("充值订单服务未配置")) case strings.HasPrefix(orderNo, constants.AgentRechargeOrderPrefix): if h.agentRechargeService != nil { if err := h.agentRechargeService.HandlePaymentCallback(ctx, orderNo, model.ProviderTypeFuiou, notify.TransactionId, orderAmt); err != nil { return c.Send(fuiou.BuildNotifyFailResponse(err.Error())) } } default: return c.Send(fuiou.BuildNotifyFailResponse("unknown order prefix")) } return c.Send(fuiou.BuildNotifySuccessResponse()) } // fuiouCallbackPayload 提取富友回调载荷,兼容 body、form req 和 query req 三种来源。 func fuiouCallbackPayload(c *fiber.Ctx) ([]byte, string) { if req := strings.TrimSpace(c.FormValue("req")); req != "" { return []byte(req), "form:req" } if req := strings.TrimSpace(c.Query("req")); req != "" { return []byte(req), "query:req" } return c.Body(), "body" }