package logic import ( "context" "fmt" "math" "strconv" "lone-services/pkg/modelbase" pkgpay "lone-services/pkg/payment" "lone-services/pkg/utils" payment "lone-services/rpc/payment/pb" "lone-services/services/payment/internal/channel" "lone-services/services/payment/internal/dao" "lone-services/services/payment/internal/model" "lone-services/services/payment/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) type PayLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewPayLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PayLogic { return &PayLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *PayLogic) Pay(in *payment.PayReq) (*payment.Response, error) { if in == nil || in.OrderSn == utils.StringEmpty { return failResponse(utils.ErrorParams), nil } loginUser := utils.GetUserFromCtx(l.ctx) if loginUser.ID < utils.NumberOne { return failResponse(utils.ErrorNoLoginInfo), nil } client := resolveClient(in.Client, loginUser.ClientCode) channelCode := resolveChannel(in.Channel) order, err := loadPayableOrder(in.OrderSn, loginUser.ID) if err != nil { l.Errorf("pay load order: %v", err) return failResponse(utils.ErrorNotFund), nil } if order.Status == dao.PayStatusOk { return &payment.Response{ Code: int32(utils.ErrorRepeatPayment.GetCode()), Msg: utils.ErrorRepeatPayment.GetMsg(), }, nil } if order.Status != dao.PayStatusWait && order.Status != dao.PayStatusIn && order.Status != dao.PayStatusFail { return outResponse(utils.Fail, "订单状态不可支付"), nil } amountFen := yuanToFen(order.PayPrice) if amountFen <= 0 { return outResponse(utils.Fail, "支付金额无效"), nil } ch, err := channel.Get(channelCode, client) if err != nil { l.Errorf("pay channel: %v", err) return outResponse(utils.Fail, err.Error()), nil } prepayReq := &pkgpay.PrepayRequest{ OutTradeNo: in.OrderSn, Description: "订单支付", AmountFen: amountFen, Currency: "CNY", Attach: in.OrderSn, } switch channelCode { case pkgpay.ChannelWechat: openid, oErr := loadOpenID(loginUser.ID) if oErr != nil || openid == utils.StringEmpty { l.Errorf("pay openid: %v", oErr) return outResponse(utils.Fail, "未绑定微信openid"), nil } prepayReq.OpenID = openid case pkgpay.ChannelAlipay, pkgpay.ChannelBank: return outResponse(utils.Fail, "支付渠道暂未开通"), nil } result, err := ch.Prepay(l.ctx, client, prepayReq) if err != nil { l.Errorf("pay prepay: %v", err) return outResponse(utils.Fail, err.Error()), nil } _ = markOrderPaying(in.OrderSn, channelCode, in.OrderSn) return okResponse(result), nil } func loadPayableOrder(orderSn string, userID int64) (*dao.OrderPayRow, error) { var row dao.OrderPayRow err := model.OrderModel{}.Init().GetOne(modelbase.Params{ Eq: map[string]string{ "order_sn": orderSn, }, }, &row) if err != nil { return nil, err } if row.Id < utils.NumberOne { return nil, fmt.Errorf("order not found") } if row.UserId > 0 && row.UserId != userID { return nil, fmt.Errorf("order user mismatch") } return &row, nil } func loadOpenID(userID int64) (string, error) { var cred dao.UserCredential err := model.UserCredentialModel{}.Init().GetOne(modelbase.Params{ Eq: map[string]string{ "user_id": strconv.FormatInt(userID, 10), "credential_type": dao.CredentialTypeOpenid, "status": strconv.Itoa(int(dao.CredentialEnabled)), }, }, &cred) if err != nil { return "", err } if cred.Id < utils.NumberOne { return "", fmt.Errorf("openid credential not found") } return cred.Identifier, nil } func markOrderPaying(orderSn, channelCode, outTradeNo string) error { payType := dao.ChannelToPayType(channelCode) updates := map[string]any{ "status": dao.PayStatusIn, "out_trade_no": outTradeNo, } if payType > 0 { updates["pay_type"] = payType } _, err := model.OrderModel{}.Init().Edit(modelbase.Params{ Eq: map[string]string{"order_sn": orderSn}, }, updates) return err } func yuanToFen(yuan float64) int64 { return int64(math.Round(yuan * 100)) }