优化支付退款钱包链路

This commit is contained in:
yml
2026-06-03 21:47:04 +08:00
parent 04193ae687
commit c80d3f960e
26 changed files with 1342 additions and 504 deletions
+9
View File
@@ -72,6 +72,15 @@ type AdminActionRequest struct {
type AuditMeta = auditlog.Meta
type RefundStatusDTO struct {
OrderID uint64 `json:"order_id"`
OrderNo string `json:"order_no"`
RefundStatus string `json:"refund_status"`
RefundAmountCent int64 `json:"refund_amount_cent"`
RefundedAt *time.Time `json:"refunded_at,omitempty"`
TotalAmount float64 `json:"total_amount"`
}
type HandoffRecordDTO struct {
ID uint64 `json:"id"`
OrderID uint64 `json:"order_id"`
+28
View File
@@ -95,6 +95,32 @@ func (h *Handler) AdminMarkAbnormal(c *gin.Context) {
h.adminAction(c, h.service.AdminMarkAbnormal, gin.H{"abnormal": true})
}
func (h *Handler) AdminRefund(c *gin.Context) {
orderID, ok := parseID(c)
if !ok {
return
}
item, err := h.service.AdminRefund(orderID)
if err != nil {
writeOrderError(c, err)
return
}
response.OK(c, item)
}
func (h *Handler) AdminRefundStatus(c *gin.Context) {
orderID, ok := parseID(c)
if !ok {
return
}
item, err := h.service.AdminRefundStatus(orderID)
if err != nil {
writeOrderError(c, err)
return
}
response.OK(c, item)
}
func (h *Handler) adminAction(c *gin.Context, fn func(uint64, uint64, AdminActionRequest, AuditMeta) error, okData gin.H) {
adminID, ok := currentAdminID(c)
if !ok {
@@ -394,6 +420,8 @@ func writeOrderError(c *gin.Context, err error) {
response.BadRequest(c, "不能租用自己发布的账号")
case errors.Is(err, ErrInsufficientBalance):
response.Error(c, http.StatusConflict, "insufficient_balance", "钱包余额不足,请先充值")
case errors.Is(err, ErrChannelPaymentRequired):
response.Error(c, http.StatusGone, "channel_payment_required", "请使用第三方支付入口完成订单付款")
case errors.Is(err, ErrOrderCannotPay):
response.Error(c, http.StatusConflict, "order_cannot_pay", "当前订单不能支付")
case errors.Is(err, ErrOrderCannotCancel):
+176 -221
View File
@@ -5,6 +5,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"log"
"math"
"strconv"
"time"
@@ -20,9 +21,20 @@ import (
"gorm.io/gorm/clause"
)
// RefundFunc 由 payment 模块注入,避免 order 与 payment 形成循环依赖。
type RefundFunc func(orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
type refundAction struct {
OrderID uint64
RefundAmountCent int64
BizType string
Remark string
}
type Repository struct {
db *gorm.DB
chatRepo *chat.Repository
db *gorm.DB
chatRepo *chat.Repository
refundFunc RefundFunc
}
const defaultPendingPaymentTimeoutMinutes = 15
@@ -35,6 +47,10 @@ func (r *Repository) SetChatRepo(cr *chat.Repository) {
r.chatRepo = cr
}
func (r *Repository) SetRefundFunc(fn RefundFunc) {
r.refundFunc = fn
}
type orderPricing struct {
RentAmount float64
OwnerRentAmount float64
@@ -201,112 +217,12 @@ func (r *Repository) Create(renterID uint64, req CreateRequest) (*OrderDTO, erro
return r.FindForUser(renterID, createdID)
}
// Pay 保留旧接口兼容,但真实付款必须走 payment 模块的渠道支付入口。
func (r *Repository) Pay(userID uint64, orderID uint64) error {
var newConvID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("id = ? AND renter_id = ?", orderID, userID).
First(&order).Error; err != nil {
return err
}
if order.Status != "pending_payment" {
return ErrOrderCannotPay
}
timeoutMinutes := pendingPaymentTimeoutMinutes(tx)
if timeoutMinutes > 0 && order.CreatedAt.Before(time.Now().Add(-time.Duration(timeoutMinutes)*time.Minute)) {
return ErrOrderCannotPay
}
var listing model.RentalListing
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, order.ListingID).Error; err != nil {
return err
}
if listing.Status != "published" || listing.ReviewStatus != "approved" || !listing.InTransaction {
return ErrListingUnavailable
}
var account model.GameAccount
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, order.AccountID).Error; err != nil {
return err
}
total := order.RentAmount + order.DepositAmount
if err := wallet.AppendEntries(tx,
wallet.Entry{
UserID: order.RenterID,
OrderID: &order.ID,
Direction: "out",
Amount: total,
BalanceType: "available",
BizType: "order_pay",
BizNo: order.OrderNo,
Remark: "订单支付扣减可用余额",
},
wallet.Entry{
UserID: order.RenterID,
OrderID: &order.ID,
Direction: "in",
Amount: total,
BalanceType: "frozen",
BizType: "order_lock",
BizNo: order.OrderNo,
Remark: "订单支付冻结租金和押金",
},
); err != nil {
if errors.Is(err, wallet.ErrInsufficientBalance) {
return ErrInsufficientBalance
}
return err
}
order.Status = "pending_handoff"
order.HandoffStatus = "pending_owner"
listing.Status = "rented"
account.Status = "rented"
conv, err := chat.EnsureOrderConversation(tx, order)
if err != nil {
return err
}
newConvID = conv.ID
orderID := order.ID
if err := notification.Append(tx,
notification.Entry{
UserID: order.OwnerID,
Type: "order",
Title: "收到新的租号订单",
Content: "租客已完成支付,请尽快提交交接说明。",
BizType: "order",
BizID: &orderID,
},
notification.Entry{
UserID: order.RenterID,
Type: "order",
Title: "订单支付成功",
Content: "支付金额已冻结,等待号主提交交接说明。",
BizType: "order",
BizID: &orderID,
},
); err != nil {
return err
}
if err := tx.Save(&order).Error; err != nil {
return err
}
if err := tx.Save(&listing).Error; err != nil {
return err
}
return tx.Save(&account).Error
})
if err != nil {
return err
}
// 事务成功后推送群聊创建事件
if newConvID > 0 && r.chatRepo != nil {
r.chatRepo.NotifyNewConversation(newConvID)
}
return nil
return ErrChannelPaymentRequired
}
// ConfirmPaidFromChannel 在乐刷确认支付后推进订单状态;租客资金不进入站内钱包。
func (r *Repository) ConfirmPaidFromChannel(orderID uint64, providerBizNo string) error {
var newConvID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
@@ -333,21 +249,8 @@ func (r *Repository) ConfirmPaidFromChannel(orderID uint64, providerBizNo string
return err
}
// 租客已通过外部渠道付款,这里不写租客钱包流水。
orderID := order.ID
total := order.RentAmount + order.DepositAmount
if err := wallet.AppendEntries(tx, wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "in",
Amount: total,
BalanceType: "frozen",
BizType: "channel_order_lock",
BizNo: firstNonEmpty(providerBizNo, order.OrderNo),
Remark: "渠道支付成功冻结租金和押金",
}); err != nil {
return err
}
order.Status = "pending_handoff"
order.HandoffStatus = "pending_owner"
listing.Status = "rented"
@@ -370,7 +273,7 @@ func (r *Repository) ConfirmPaidFromChannel(orderID uint64, providerBizNo string
UserID: order.RenterID,
Type: "order",
Title: "订单支付成功",
Content: "支付金额已冻结,等待号主提交交接说明。",
Content: "支付已完成,等待号主提交交接说明。",
BizType: "order",
BizID: &orderID,
},
@@ -395,7 +298,8 @@ func (r *Repository) ConfirmPaidFromChannel(orderID uint64, providerBizNo string
}
func (r *Repository) Cancel(userID uint64, orderID uint64) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("id = ? AND renter_id = ?", orderID, userID).
@@ -419,31 +323,12 @@ func (r *Repository) Cancel(userID uint64, orderID uint64) error {
order.HandoffStatus = "cancelled"
orderID := order.ID
if beforeStatus == "pending_handoff" {
total := order.RentAmount + order.DepositAmount
if err := wallet.AppendEntries(tx,
wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "out",
Amount: total,
BalanceType: "frozen",
BizType: "order_cancel",
BizNo: order.OrderNo,
Remark: "取消订单释放冻结金额",
},
wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "in",
Amount: total,
BalanceType: "available",
BizType: "order_cancel_refund",
BizNo: order.OrderNo,
Remark: "取消订单退回可用余额",
},
); err != nil {
totalCent := int64(math.Round((order.RentAmount + order.DepositAmount) * 100))
action, err := r.prepareRefund(&order, totalCent, "cancel_refund", "取消订单原路退款")
if err != nil {
return err
}
refund = action
}
if err := notification.Append(tx,
notification.Entry{
@@ -458,7 +343,7 @@ func (r *Repository) Cancel(userID uint64, orderID uint64) error {
UserID: order.RenterID,
Type: "order",
Title: "订单取消成功",
Content: "订单已取消,相关金额已释放。",
Content: "订单已取消,退款将原路退回您的支付账户。",
BizType: "order",
BizID: &orderID,
},
@@ -476,6 +361,11 @@ func (r *Repository) Cancel(userID uint64, orderID uint64) error {
}
return tx.Save(&account).Error
})
if err != nil {
return err
}
r.startRefundBestEffort(refund)
return nil
}
func (r *Repository) SubmitHandoff(userID uint64, orderID uint64, req SubmitHandoffRequest) (*HandoffRecordDTO, error) {
@@ -651,7 +541,8 @@ func (r *Repository) ConfirmReturn(userID uint64, orderID uint64) error {
}
func (r *Repository) ConfirmCheckout(userID uint64, orderID uint64) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -677,8 +568,15 @@ func (r *Repository) ConfirmCheckout(userID uint64, orderID uint64) error {
}
checkout.Status = "accepted"
checkout.OwnerAdjustedAt = &now
return r.finalizeCheckout(tx, &order, &checkout, "号主已确认结账,订单完成。")
action, err := r.finalizeCheckout(tx, &order, &checkout, "号主已确认结账,订单完成。")
refund = action
return err
})
if err != nil {
return err
}
r.startRefundBestEffort(refund)
return nil
}
func (r *Repository) CounterCheckout(userID uint64, orderID uint64, req CounterCheckoutRequest) (*CheckoutDTO, error) {
@@ -756,7 +654,8 @@ func (r *Repository) CounterCheckout(userID uint64, orderID uint64, req CounterC
}
func (r *Repository) AcceptCheckout(userID uint64, orderID uint64) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -777,8 +676,15 @@ func (r *Repository) AcceptCheckout(userID uint64, orderID uint64) error {
now := time.Now()
checkout.Status = "accepted"
checkout.RenterConfirmedAt = &now
return r.finalizeCheckout(tx, &order, &checkout, "租客已确认修正结账,订单完成。")
action, err := r.finalizeCheckout(tx, &order, &checkout, "租客已确认修正结账,订单完成。")
refund = action
return err
})
if err != nil {
return err
}
r.startRefundBestEffort(refund)
return nil
}
func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) {
@@ -840,7 +746,8 @@ func (r *Repository) HandoffRecordsAdmin(orderID uint64) ([]HandoffRecordDTO, er
}
func (r *Repository) AdminClose(adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
order, listing, account, err := r.findOrderAssetsForAdminUpdate(tx, orderID)
if err != nil {
return err
@@ -862,38 +769,19 @@ func (r *Repository) AdminClose(adminID uint64, orderID uint64, req AdminActionR
listing.InTransaction = false
account.Status = "offline"
if beforeOrderStatus != "pending_payment" {
total := order.RentAmount + order.DepositAmount
if err := wallet.AppendEntries(tx,
wallet.Entry{
UserID: order.RenterID,
OrderID: &order.ID,
Direction: "out",
Amount: total,
BalanceType: "frozen",
BizType: "admin_order_close",
BizNo: order.OrderNo,
Remark: "后台关闭订单释放冻结金额",
},
wallet.Entry{
UserID: order.RenterID,
OrderID: &order.ID,
Direction: "in",
Amount: total,
BalanceType: "available",
BizType: "admin_order_close_refund",
BizNo: order.OrderNo,
Remark: "后台关闭订单退回可用余额",
},
); err != nil {
totalCent := int64(math.Round((order.RentAmount + order.DepositAmount) * 100))
action, err := r.prepareRefund(order, totalCent, "admin_close_refund", "客服关闭订单原路退款")
if err != nil {
return err
}
refund = action
}
if err := notification.Append(tx,
notification.Entry{
UserID: order.RenterID,
Type: "order_admin",
Title: "订单已由客服关闭",
Content: "客服已关闭订单,模拟冻结金额已释放。原因:" + req.Reason,
Content: "客服已关闭订单,退款将原路退回您的支付账户。原因:" + req.Reason,
BizType: "order",
BizID: &order.ID,
},
@@ -935,6 +823,11 @@ func (r *Repository) AdminClose(adminID uint64, orderID uint64, req AdminActionR
}
return tx.Save(account).Error
})
if err != nil {
return err
}
r.startRefundBestEffort(refund)
return nil
}
func (r *Repository) AdminMarkAbnormal(adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
@@ -1002,6 +895,57 @@ func (r *Repository) AdminMarkAbnormal(adminID uint64, orderID uint64, req Admin
})
}
// AdminRefund 触发后台人工退款,退款由 payment 模块走渠道原路退回。
func (r *Repository) AdminRefund(orderID uint64) (*RefundStatusDTO, error) {
var order model.RentalOrder
if err := r.db.First(&order, orderID).Error; err != nil {
return nil, err
}
if order.RefundStatus == "refunded" {
return r.buildRefundStatusDTO(&order), nil
}
if r.refundFunc == nil {
return nil, ErrDependencyUnavailable
}
totalCent := int64(math.Round((order.RentAmount + order.DepositAmount) * 100))
if totalCent <= 0 {
return nil, ErrInvalidCheckoutAmount
}
status, err := r.refundFunc(orderID, totalCent, "admin_refund", "后台人工退款")
if err != nil {
return nil, err
}
// 重新读取订单,拿到 payment 模块更新后的退款字段。
if err := r.db.First(&order, orderID).Error; err != nil {
return nil, err
}
dto := r.buildRefundStatusDTO(&order)
if status != "" {
dto.RefundStatus = status
}
return dto, nil
}
// AdminRefundStatus 查询订单退款状态。
func (r *Repository) AdminRefundStatus(orderID uint64) (*RefundStatusDTO, error) {
var order model.RentalOrder
if err := r.db.First(&order, orderID).Error; err != nil {
return nil, err
}
return r.buildRefundStatusDTO(&order), nil
}
func (r *Repository) buildRefundStatusDTO(order *model.RentalOrder) *RefundStatusDTO {
return &RefundStatusDTO{
OrderID: order.ID,
OrderNo: order.OrderNo,
RefundStatus: order.RefundStatus,
RefundAmountCent: order.RefundAmountCent,
RefundedAt: order.RefundedAt,
TotalAmount: order.RentAmount + order.DepositAmount,
}
}
func (r *Repository) FindForUser(userID uint64, orderID uint64) (*OrderDTO, error) {
var row orderRow
if err := r.baseQuery().
@@ -1014,14 +958,14 @@ func (r *Repository) FindForUser(userID uint64, orderID uint64) (*OrderDTO, erro
return &dto, nil
}
func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, checkout *model.OrderCheckout, renterContent string) error {
func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, checkout *model.OrderCheckout, renterContent string) (*refundAction, error) {
var listing model.RentalListing
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, order.ListingID).Error; err != nil {
return err
return nil, err
}
var account model.GameAccount
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, order.AccountID).Error; err != nil {
return err
return nil, err
}
now := time.Now()
order.Status = "completed"
@@ -1034,20 +978,11 @@ func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, che
account.Status = "published"
orderID := order.ID
settlement := buildCheckoutSettlement(*order, checkout)
entries := []wallet.Entry{
wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "out",
Amount: order.RentAmount + order.DepositAmount,
BalanceType: "frozen",
BizType: "order_settle",
BizNo: order.OrderNo,
Remark: "订单结账释放冻结金额",
},
}
// 卖家收入进入站内钱包;租客资金不进入站内钱包。
var ownerEntries []wallet.Entry
if settlement.OwnerRentIncome > 0 {
entries = append(entries, wallet.Entry{
ownerEntries = append(ownerEntries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
@@ -1059,7 +994,7 @@ func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, che
})
}
if settlement.DepositCompensation > 0 {
entries = append(entries, wallet.Entry{
ownerEntries = append(ownerEntries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
@@ -1070,33 +1005,23 @@ func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, che
Remark: "订单结账押金赔付",
})
}
if settlement.RentRefund > 0 {
entries = append(entries, wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "in",
Amount: settlement.RentRefund,
BalanceType: "available",
BizType: "rent_refund",
BizNo: order.OrderNo,
Remark: "订单结账退回未使用租金",
})
if len(ownerEntries) > 0 {
if err := wallet.AppendEntries(tx, ownerEntries...); err != nil {
return nil, err
}
}
if settlement.DepositRefund > 0 {
entries = append(entries, wallet.Entry{
UserID: order.RenterID,
OrderID: &orderID,
Direction: "in",
Amount: settlement.DepositRefund,
BalanceType: "available",
BizType: "deposit_release",
BizNo: order.OrderNo,
Remark: "订单结账退回押金",
})
}
if err := wallet.AppendEntries(tx, entries...); err != nil {
return err
var refund *refundAction
renterRefundTotal := settlement.RentRefund + settlement.DepositRefund
if renterRefundTotal > 0 {
refundCent := int64(math.Round(renterRefundTotal * 100))
action, err := r.prepareRefund(order, refundCent, "checkout_refund", "结账退款原路退还")
if err != nil {
return nil, err
}
refund = action
}
checkout.RentAmount = settlement.ActualRentAmount
checkout.OwnerRentAmount = settlement.OwnerRentIncome
checkout.PlatformFee = settlement.PlatformFee
@@ -1120,18 +1045,48 @@ func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, che
BizID: &orderID,
},
); err != nil {
return err
return nil, err
}
if err := tx.Save(order).Error; err != nil {
return err
return nil, err
}
if err := tx.Save(checkout).Error; err != nil {
return err
return nil, err
}
if err := tx.Save(&listing).Error; err != nil {
return err
return nil, err
}
if err := tx.Save(&account).Error; err != nil {
return nil, err
}
return refund, nil
}
func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, bizType string, remark string) (*refundAction, error) {
if amountCent <= 0 {
return nil, nil
}
if r.refundFunc == nil {
return nil, ErrDependencyUnavailable
}
order.RefundStatus = "pending"
order.RefundAmountCent = amountCent
order.RefundedAt = nil
return &refundAction{
OrderID: order.ID,
RefundAmountCent: amountCent,
BizType: bizType,
Remark: remark,
}, nil
}
func (r *Repository) startRefundBestEffort(action *refundAction) {
if action == nil || r.refundFunc == nil {
return
}
if _, err := r.refundFunc(action.OrderID, action.RefundAmountCent, action.BizType, action.Remark); err != nil {
log.Printf("[order] start refund failed order_id=%d biz_type=%s amount_cent=%d err=%v", action.OrderID, action.BizType, action.RefundAmountCent, err)
}
return tx.Save(&account).Error
}
func (r *Repository) findOrderAssetsForAdminUpdate(tx *gorm.DB, orderID uint64) (*model.RentalOrder, *model.RentalListing, *model.GameAccount, error) {
+37 -16
View File
@@ -3,22 +3,23 @@ package order
import "errors"
var (
ErrDependencyUnavailable = errors.New("dependency unavailable")
ErrInvalidRentHours = errors.New("invalid rent hours")
ErrListingUnavailable = errors.New("listing unavailable")
ErrCannotRentOwnListing = errors.New("cannot rent own listing")
ErrInsufficientBalance = errors.New("insufficient balance")
ErrOrderCannotPay = errors.New("order cannot pay")
ErrOrderCannotCancel = errors.New("order cannot cancel")
ErrOrderCannotHandoff = errors.New("order cannot handoff")
ErrOrderCannotReceive = errors.New("order cannot receive")
ErrOrderCannotReturn = errors.New("order cannot return")
ErrOrderCannotComplete = errors.New("order cannot complete")
ErrCheckoutCannotSubmit = errors.New("checkout cannot submit")
ErrCheckoutCannotConfirm = errors.New("checkout cannot confirm")
ErrCheckoutCannotCounter = errors.New("checkout cannot counter")
ErrInvalidCheckoutAmount = errors.New("invalid checkout amount")
ErrPermissionDenied = errors.New("permission denied")
ErrDependencyUnavailable = errors.New("dependency unavailable")
ErrInvalidRentHours = errors.New("invalid rent hours")
ErrListingUnavailable = errors.New("listing unavailable")
ErrCannotRentOwnListing = errors.New("cannot rent own listing")
ErrInsufficientBalance = errors.New("insufficient balance")
ErrOrderCannotPay = errors.New("order cannot pay")
ErrChannelPaymentRequired = errors.New("channel payment required")
ErrOrderCannotCancel = errors.New("order cannot cancel")
ErrOrderCannotHandoff = errors.New("order cannot handoff")
ErrOrderCannotReceive = errors.New("order cannot receive")
ErrOrderCannotReturn = errors.New("order cannot return")
ErrOrderCannotComplete = errors.New("order cannot complete")
ErrCheckoutCannotSubmit = errors.New("checkout cannot submit")
ErrCheckoutCannotConfirm = errors.New("checkout cannot confirm")
ErrCheckoutCannotCounter = errors.New("checkout cannot counter")
ErrInvalidCheckoutAmount = errors.New("invalid checkout amount")
ErrPermissionDenied = errors.New("permission denied")
)
const internalOrderHours = 24
@@ -181,6 +182,26 @@ func (s *Service) AdminMarkAbnormal(adminID uint64, orderID uint64, req AdminAct
return s.repo.AdminMarkAbnormal(adminID, orderID, req, meta)
}
func (s *Service) AdminRefund(orderID uint64) (*RefundStatusDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 {
return nil, ErrOrderCannotComplete
}
return s.repo.AdminRefund(orderID)
}
func (s *Service) AdminRefundStatus(orderID uint64) (*RefundStatusDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 {
return nil, ErrOrderCannotComplete
}
return s.repo.AdminRefundStatus(orderID)
}
func (s *Service) FindForUser(userID uint64, orderID uint64) (*OrderDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable