160 lines
4.1 KiB
Go
160 lines
4.1 KiB
Go
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))
|
|
}
|