package payment import ( "context" "hfb_sys/backend/internal/model" "time" "gorm.io/gorm" "gorm.io/gorm/clause" ) func (r *Repository) applyChannelStatus(ctx context.Context, payment *model.PaymentOrder, status string, payTime string, raw map[string]string, source string) error { switch status { case "paid": paidAt := parseChannelTime(payTime) if paidAt == nil { now := time.Now() paidAt = &now } return r.confirmPaid(ctx, payment, status, *paidAt, raw, source) case "closed": return r.updateChannelStatus(ctx, payment.ID, "closed", raw, source) case "failed": return r.updateChannelStatus(ctx, payment.ID, "failed", raw, source) default: return r.updateChannelStatus(ctx, payment.ID, "paying", raw, source) } } func (r *Repository) updateChannelStatus(ctx context.Context, paymentID uint64, status string, raw map[string]string, source string) error { updates := map[string]any{ "status": status, "raw_response": jsonMap(withRawSource(raw, source)), } if source == channelSourceNotify { updates["notified_at"] = time.Now() } return r.db.WithContext(ctx).Model(&model.PaymentOrder{}). Where("id = ? AND status NOT IN ?", paymentID, []string{"paid", "closed", "failed"}). Updates(updates).Error } // isOrderPaymentTerminalStatus 判断订单支付流水是否已进入终态,终态不再被查询或普通回调刷回支付中。 func isOrderPaymentTerminalStatus(status string) bool { return status == "paid" || status == "closed" || status == "failed" } func (r *Repository) confirmPaid(ctx context.Context, payment *model.PaymentOrder, status string, paidAt time.Time, raw map[string]string, source string) error { var newConvID uint64 var notifyFn func(uint64) err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { if payment.OrderID != 0 { switch payment.BizType { case "mohong_pay": if r.mohongRepo == nil { return ErrDependencyUnavailable } convID, err := r.mohongRepo.ConfirmPaidFromChannelTx(tx, payment.OrderID) if err != nil { return err } newConvID = convID notifyFn = r.mohongRepo.NotifyNewConversation default: if r.orderRepo == nil { return ErrDependencyUnavailable } convID, err := r.orderRepo.ConfirmPaidFromChannelTx(tx, payment.OrderID) if err != nil { return err } newConvID = convID notifyFn = r.orderRepo.NotifyNewConversation } } var latest model.PaymentOrder if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&latest, payment.ID).Error; err != nil { return err } updates := map[string]any{ "status": "paid", "provider_order_id": firstNonEmpty(raw["provider_order_id"], raw["schc_order_id"], raw["leshua_order_id"], raw["pay_order_no"], raw["trade_no"], latest.ProviderOrderID), "raw_response": jsonMap(withRawSource(raw, source)), "paid_at": paidAt, } if source == channelSourceNotify { updates["notified_at"] = time.Now() } return tx.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(updates).Error }) if err != nil { return err } if newConvID > 0 && notifyFn != nil { notifyFn(newConvID) } if latest, err := r.findPaymentByID(ctx, payment.ID); err == nil { if runtimeConfig, err := r.runtimeConfigForPayment(ctx, latest); err == nil { r.recordConfigUsage(ctx, runtimeConfig, latest) } } return nil } func (r *Repository) markPaymentFailed(ctx context.Context, paymentID uint64, raw map[string]string, message string) error { if raw == nil { raw = map[string]string{"error": message} } return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(map[string]any{ "status": "failed", "raw_response": jsonMap(raw), }).Error }