package notify import ( "context" "encoding/json" "io" "net/http" commonLogic "github.com/perfect-panel/server/internal/logic/common" "github.com/perfect-panel/server/pkg/constant" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" "github.com/hibiken/asynq" "github.com/perfect-panel/server/internal/model/payment" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/payment/stripe" "github.com/perfect-panel/server/queue/types" ) type StripeNotifyLogic struct { logger.Logger ctx context.Context svcCtx *svc.ServiceContext } // NewStripeNotifyLogic Stripe notify func NewStripeNotifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *StripeNotifyLogic { return &StripeNotifyLogic{ Logger: logger.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *StripeNotifyLogic) StripeNotify(r *http.Request, w http.ResponseWriter) error { const MaxBodyBytes = int64(65536) r.Body = http.MaxBytesReader(w, r.Body, MaxBodyBytes) payload, err := io.ReadAll(r.Body) if err != nil { l.Errorw("[StripeNotify] error", logger.Field("errors", err.Error())) return err } signature := r.Header.Get("Stripe-Signature") stripeConfig, ok := l.ctx.Value(constant.CtxKeyPayment).(*payment.Payment) if !ok { return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "payment config not found") } config := payment.StripeConfig{} if err := json.Unmarshal([]byte(stripeConfig.Config), &config); err != nil { return err } client := stripe.NewClient(stripe.Config{ PublicKey: config.PublicKey, SecretKey: config.SecretKey, WebhookSecret: config.WebhookSecret, }) notify, err := client.ParseNotify(payload, signature) if err != nil { l.Errorw("[StripeNotify] error", logger.Field("errors", err.Error())) return err } orderInfo, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, notify.OrderNo) if err != nil { l.Logger.Error("[StripeNotify] Find order failed", logger.Field("error", err.Error()), logger.Field("orderNo", notify.OrderNo)) return errors.Wrapf(xerr.NewErrCode(xerr.OrderNotExist), "order not exist: %v", notify.OrderNo) } commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "payment_notify_received", "[SubscriptionFlow] stripe notify received", append(commonLogic.OrderTraceFields(orderInfo), logger.Field("payment_platform", stripeConfig.Platform), logger.Field("stripe_event_type", notify.EventType), )..., ) if notify.EventType == "payment_intent.succeeded" { if orderInfo.Status == 5 { return nil } // update order status err = l.svcCtx.OrderModel.UpdateOrderStatus(l.ctx, notify.OrderNo, 2) if err != nil { return err } commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "payment_settled", "[SubscriptionFlow] stripe notify marked order as paid", append(commonLogic.OrderTraceFields(orderInfo), logger.Field("payment_platform", stripeConfig.Platform), logger.Field("stripe_event_type", notify.EventType), )..., ) // create ActivateOrder task payload := types.ForthwithActivateOrderPayload{ OrderNo: notify.OrderNo, } bytes, err := json.Marshal(payload) if err != nil { l.Errorw("[StripeNotify] Marshal error", logger.Field("errors", err.Error()), logger.Field("payload", payload)) return err } task := asynq.NewTask(types.ForthwithActivateOrder, bytes, asynq.MaxRetry(5)) taskInfo, err := l.svcCtx.Queue.Enqueue(task) if err != nil { l.Errorw("[StripeNotify] Enqueue error", logger.Field("errors", err.Error())) return err } commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "activation_task_enqueued", "[SubscriptionFlow] activation task enqueued from stripe notify", append(commonLogic.OrderTraceFields(orderInfo), logger.Field("payment_platform", stripeConfig.Platform), logger.Field("stripe_event_type", notify.EventType), logger.Field("queue_task_id", taskInfo.ID), )..., ) l.Infow("[StripeNotify] success", logger.Field("orderNo", notify.OrderNo)) } return nil }