package service import ( "context" "errors" "fmt" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" "gorm.io/gorm" "hepa-calc-admin-api/api/dao" "hepa-calc-admin-api/api/model" "hepa-calc-admin-api/config" "hepa-calc-admin-api/extend/weChat" "hepa-calc-admin-api/global" "hepa-calc-admin-api/utils" "strconv" "time" ) type OrderSingleService struct { } // CancelOrderSingle 取消单项订单 // cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, orderSingle *model.OrderSingle, refundAmount *float64, cancelReason int) (bool, error) { // 检测多次请求 redisKey := "CancelOrderSingle" + fmt.Sprintf("%d", orderSingle.UserId) + fmt.Sprintf("%d", orderSingle.OrderId) res, _ := global.Redis.Get(context.Background(), redisKey).Result() if res != "" { return false, errors.New("请勿重复操作") } defer func(redisKey string) { global.Redis.Del(context.Background(), redisKey) }(redisKey) // 添加缓存 _, err := global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() if err != nil { return false, errors.New("取消订单失败") } // 订单状态(1:待支付 2:已完成 3:已取消) if orderSingle.OrderStatus == 2 { return false, errors.New("订单已完成,无法取消") } if orderSingle.OrderStatus == 3 { return false, errors.New("订单已取消,请勿重复操作") } // 取消状态(0:否 1:是) if orderSingle.CancelStatus == 1 { return false, errors.New("订单已取消") } // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) if orderSingle.PayStatus == 2 { return false, errors.New("订单已支付,无法取消") } // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) if orderSingle.RefundStatus == 1 { return false, errors.New("订单已申请退款") } if orderSingle.RefundStatus == 2 { return false, errors.New("订单退款中") } if orderSingle.RefundStatus == 3 { return false, errors.New("订单已退款成功") } if orderSingle.RefundStatus == 6 { return false, errors.New("订单退款异常") } // 修改订单为取消 orderSingleData := make(map[string]interface{}) orderSingleData["order_status"] = 3 if cancelReason == 3 { // 支付超时取消 orderSingleData["pay_status"] = 5 } orderSingleData["cancel_status"] = 1 orderSingleData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05") orderSingleData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason) orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") orderSingleDao := dao.OrderSingleDao{} err = orderSingleDao.EditOrderSingleById(tx, orderSingle.OrderId, orderSingleData) if err != nil { return false, errors.New("订单取消失败") } // 退还订单优惠卷 err = r.OrderCouponRefund(tx, orderSingle) if err != nil { tx.Rollback() return false, err } // 订单退款 _, err = r.OrderRefund(tx, orderSingle, refundAmount, utils.OrderCancelReasonToString(cancelReason)) if err != nil { tx.Rollback() return false, err } return true, nil } // GetJsapiPrepay 获取jsapi预支付交易会话标识 func (r *OrderSingleService) GetJsapiPrepay(m *model.OrderSingle) (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) { // 获取用户数据 userDao := dao.UserDao{} user, err := userDao.GetUserById(m.UserId) if err != nil || user == nil { return nil, errors.New("用户错误") } if user.OpenId == "" { return nil, errors.New("发起支付失败") } jsapiRequest := weChat.JsapiRequest{ AppId: config.C.Wechat.AppId, MchId: config.C.Wechat.Pay1281030301.MchId, Description: "肝病算一算", OutTradeNo: m.OrderNo, NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.SingleRefundNotifyUrl, Amount: weChat.JsapiRequestAmountRequest{ Total: int64(m.PaymentAmountTotal * 100), Currency: "CNY", }, Payer: weChat.JsapiRequestPayerRequest{OpenId: user.OpenId}, } prepay, err = jsapiRequest.GetJsapiPrepay() if err != nil { return nil, err } return prepay, nil } // GetAppPrepay 获取app预支付交易会话标识 func (r *OrderSingleService) GetAppPrepay(m *model.OrderSingle) (prepay *app.PrepayWithRequestPaymentResponse, err error) { // 获取用户数据 userDao := dao.UserDao{} user, err := userDao.GetUserById(m.UserId) if err != nil || user == nil { return nil, errors.New("用户错误") } if user.OpenId == "" { return nil, errors.New("发起支付失败") } appRequest := weChat.AppRequest{ AppId: config.C.Wechat.AppId, MchId: config.C.Wechat.Pay1281030301.MchId, Description: "肝病算一算", OutTradeNo: m.OrderNo, NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.SingleRefundNotifyUrl, Amount: weChat.AppRequestAmountRequest{ Total: int64(m.PaymentAmountTotal * 100), Currency: "CNY", }, } prepay, err = appRequest.GetAppPrepay() if err != nil { return nil, err } return prepay, nil } // CompleteUnPayOrderSingle 完成未支付单项订单-开通会员成功时使用 func (r *OrderSingleService) CompleteUnPayOrderSingle(tx *gorm.DB, userId int64) (bool, error) { // 获取所有未支付单项订单 orderSingleDao := dao.OrderSingleDao{} maps := make(map[string]interface{}) maps["user_id"] = userId maps["order_status"] = 1 maps["pay_status"] = 1 maps["cancel_status"] = 0 orderSingles, err := orderSingleDao.GetOrderSingleList(maps) if err != nil { return false, err } for _, single := range orderSingles { // 生成第三方支付流水号 escrowTradeNo := "GD" + global.Snowflake.Generate().String() orderSingleData := make(map[string]interface{}) orderSingleData["order_status"] = 2 orderSingleData["pay_status"] = 2 orderSingleData["pay_time"] = time.Now().Format("2006-01-02 15:04:05") orderSingleData["escrow_trade_no"] = escrowTradeNo orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") err = orderSingleDao.EditOrderSingleById(tx, single.OrderId, orderSingleData) if err != nil { return false, err } // 增加题目支付次数 questionDao := dao.QuestionDao{} err = questionDao.Inc(tx, single.QuestionId, "pay_count", 1) if err != nil { return false, err } } return true, nil } // OrderRefund 订单退款 func (r *OrderSingleService) OrderRefund(tx *gorm.DB, order *model.OrderSingle, refundAmount *float64, refundReason string) (*model.OrderSingleRefund, error) { var refundStatus int var successTime time.Time var refundId string // 判断订单支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) if order.PayStatus != 2 { // 非已支付,无需退款 return nil, nil } // 检测退款金额 if *refundAmount > order.PaymentAmountTotal { return nil, errors.New("退款金额不可超过实付金额") } // 退款编号 refundNo := strconv.FormatInt(global.Snowflake.Generate().Int64(), 10) // 退款金额为0 if *refundAmount <= 0 { // 支付金额为0,模拟退款 refundId = "模拟退款:" + strconv.FormatInt(global.Snowflake.Generate().Int64(), 10) refundStatus = 3 successTime = time.Now() } // 退款金额大于0 if *refundAmount > 0 { refundRequest := weChat.RefundRequest{ TransactionId: order.EscrowTradeNo, OutTradeNo: order.OrderNo, OutRefundNo: refundNo, Reason: refundReason, RefundAmount: int64(*refundAmount * 100), PaymentAmountTotal: int64(order.PaymentAmountTotal * 100), NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.SingleRefundNotifyUrl, } refund, err := refundRequest.Refund() if err != nil { return nil, err } if refund.Status == nil { return nil, errors.New("退款状态错误") } // 处理订单状态 switch *refund.Status { case "SUCCESS": // 退款成功 refundStatus = 3 if refund.SuccessTime != nil { successTime = *refund.SuccessTime } case "CLOSED": // 退款关闭 refundStatus = 5 case "PROCESSING": // 退款处理中 refundStatus = 2 case "ABNORMAL": // 退款异常 return nil, errors.New("退款状态错误") default: return nil, errors.New("退款状态错误") } if refund.RefundId == nil { return nil, errors.New("缺少退款订单编号") } // 退款编号 refundId = *refund.RefundId } // 新增退款表 orderRefund := &model.OrderSingleRefund{ UserId: order.UserId, OrderId: order.OrderId, OrderNo: order.OrderNo, RefundNo: refundNo, RefundId: refundId, RefundStatus: refundStatus, RefundTotal: *refundAmount, RefundReason: refundReason, } if refundStatus == 3 && !successTime.IsZero() { t := model.LocalTime(successTime) orderRefund.SuccessTime = &t } orderSingleRefundDao := dao.OrderSingleRefundDao{} orderSingleRefund, err := orderSingleRefundDao.AddOrderSingleRefund(tx, orderRefund) if err != nil || orderSingleRefund == nil { return nil, errors.New(err.Error()) } return orderSingleRefund, nil } // OrderCouponRefund 订单优惠卷退还 func (r *OrderSingleService) OrderCouponRefund(tx *gorm.DB, order *model.OrderSingle) error { // 订单优惠金额为0,无需退还优惠卷 if order.CouponAmountTotal == 0 { return nil } // 获取订单优惠卷数据 orderSingleCouponDao := dao.OrderSingleCouponDao{} orderSingleCoupons, err := orderSingleCouponDao.GetOrderSingleCouponListByOrderId(order.OrderId) if err != nil { return errors.New("优惠卷数据错误") } userCouponService := &UserCouponService{} for _, coupon := range orderSingleCoupons { _ = userCouponService.ReturnUserCoupon(tx, coupon.UserCouponId) } return nil }