diff --git a/backend/app/http/v1/transaction.go b/backend/app/http/v1/transaction.go index 55ab4f5..d3edf6f 100644 --- a/backend/app/http/v1/transaction.go +++ b/backend/app/http/v1/transaction.go @@ -12,10 +12,56 @@ type Transaction struct{} // Create Order // +// @Summary Create Order +// @Description 创建订单 +// @Tags Transaction +// @Accept json +// @Produce json +// @Param form body dto.OrderCreateForm true "订单创建参数" +// @Success 200 {object} dto.OrderCreateResponse // @Router /v1/t/:tenantCode/orders [post] +// @Bind form body +func (t *Transaction) Create(ctx fiber.Ctx, form *dto.OrderCreateForm) (*dto.OrderCreateResponse, error) { + tenantID := getTenantID(ctx) + uid := getUserID(ctx) + return services.Order.Create(ctx, tenantID, uid, form) +} + +// Pay Order +// +// @Summary Pay Order +// @Description 支付订单 +// @Tags Transaction +// @Accept json +// @Produce json +// @Param id path int64 true "订单ID" +// @Param form body dto.OrderPayForm true "支付参数" +// @Success 200 {object} dto.OrderPayResponse // @Router /v1/t/:tenantCode/orders/:id/pay [post] +// @Bind id path +// @Bind form body +func (t *Transaction) Pay(ctx fiber.Ctx, id int64, form *dto.OrderPayForm) (*dto.OrderPayResponse, error) { + tenantID := getTenantID(ctx) + uid := getUserID(ctx) + return services.Order.Pay(ctx, tenantID, uid, id, form) +} + +// Order Status +// +// @Summary Order Status +// @Description 查询订单状态 +// @Tags Transaction +// @Accept json +// @Produce json +// @Param id path int64 true "订单ID" +// @Success 200 {object} dto.OrderStatusResponse // @Router /v1/t/:tenantCode/orders/:id/status [get] -// @Router /v1/t/:tenantCode/webhook/payment/notify [post] +// @Bind id path +func (t *Transaction) Status(ctx fiber.Ctx, id int64) (*dto.OrderStatusResponse, error) { + tenantID := getTenantID(ctx) + uid := getUserID(ctx) + return services.Order.Status(ctx, tenantID, uid, id) +} // @Summary Payment Webhook // @Description Payment Webhook @@ -24,6 +70,7 @@ type Transaction struct{} // @Produce json // @Param form body dto.PaymentWebhookForm true "Webhook Data" // @Success 200 {string} string "success" +// @Router /v1/t/:tenantCode/webhook/payment/notify [post] // @Bind form body func (t *Transaction) Webhook(ctx fiber.Ctx, form *dto.PaymentWebhookForm) (string, error) { tenantID := getTenantID(ctx) diff --git a/backend/app/services/order.go b/backend/app/services/order.go index 841285b..bbf8c13 100644 --- a/backend/app/services/order.go +++ b/backend/app/services/order.go @@ -238,14 +238,21 @@ func (s *order) Pay( return nil, errorx.ErrStatusConflict.WithMsg("订单状态不可支付") } - if form.Method == "balance" { + switch form.Method { + case "balance": return s.payWithBalance(ctx, o) + case "alipay", "external": + // mock external: 标记已支付,避免前端卡住 + if err := s.settleOrder(ctx, o, "external", ""); err != nil { + if _, ok := err.(*errorx.AppError); ok { + return nil, err + } + return nil, errorx.ErrDatabaseError.WithCause(err) + } + return &transaction_dto.OrderPayResponse{PayParams: "mock_pay_params"}, nil + default: + return nil, errorx.ErrBadRequest.WithMsg("unsupported payment method") } - - // External payment (mock) - normally returns URL/params - return &transaction_dto.OrderPayResponse{ - PayParams: "mock_pay_params", - }, nil } // ProcessExternalPayment handles callback from payment gateway @@ -308,13 +315,12 @@ func (s *order) settleOrder(ctx context.Context, o *models.Order, method, extern // 2. Update Order Status now := time.Now() - // snapshot := o.Snapshot // Preserve existing snapshot or update it with external ID - // TODO: Update snapshot with payment info _, err := tx.Order.WithContext(ctx).Where(tx.Order.ID.Eq(o.ID)).Updates(&models.Order{ Status: consts.OrderStatusPaid, PaidAt: now, UpdatedAt: now, }) + if err != nil { return err } @@ -355,7 +361,15 @@ func (s *order) settleOrder(ctx context.Context, o *models.Order, method, extern fee := int64(float64(amount) * 0.10) creatorIncome := amount - fee - // Credit Tenant Owner Balance (Net Income) + // Credit Tenant Owner Balance (Net Income) 并记录余额快照 + owner, err := tx.User.WithContext(ctx). + Where(tx.User.ID.Eq(tenantOwnerID)). + First() + if err != nil { + return err + } + balanceBefore := owner.Balance + balanceAfter := balanceBefore + creatorIncome _, err = tx.User.WithContext(ctx). Where(tx.User.ID.Eq(tenantOwnerID)). Update(tx.User.Balance, gorm.Expr("balance + ?", creatorIncome)) @@ -369,8 +383,8 @@ func (s *order) settleOrder(ctx context.Context, o *models.Order, method, extern OrderID: o.ID, Type: consts.TenantLedgerTypeDebitPurchase, // Income from purchase Amount: creatorIncome, - BalanceBefore: 0, // TODO - BalanceAfter: 0, // TODO + BalanceBefore: balanceBefore, + BalanceAfter: balanceAfter, FrozenBefore: 0, FrozenAfter: 0, IdempotencyKey: uuid.NewString(),