feat: order rpc
This commit is contained in:
@@ -7,12 +7,13 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -4,13 +4,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"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"
|
||||
@@ -51,7 +49,7 @@ func (l *NotifyLogic) Notify(in *payment.NotifyReq) (*payment.Response, error) {
|
||||
return outResponse(utils.Fail, "支付未成功"), nil
|
||||
}
|
||||
|
||||
if err := markOrderPaid(result); err != nil {
|
||||
if err := l.markOrderPaid(result); err != nil {
|
||||
l.Errorf("notify mark paid: %v", err)
|
||||
return outResponse(utils.Fail, err.Error()), nil
|
||||
}
|
||||
@@ -59,38 +57,20 @@ func (l *NotifyLogic) Notify(in *payment.NotifyReq) (*payment.Response, error) {
|
||||
return okResponse(result), nil
|
||||
}
|
||||
|
||||
func markOrderPaid(result *pkgpay.NotifyResult) error {
|
||||
func (l *NotifyLogic) markOrderPaid(result *pkgpay.NotifyResult) error {
|
||||
if result.OutTradeNo == utils.StringEmpty {
|
||||
return errors.New("out_trade_no empty")
|
||||
}
|
||||
|
||||
var row dao.OrderPayRow
|
||||
err := model.OrderModel{}.Init().GetOne(modelbase.Params{
|
||||
Eq: map[string]string{"order_sn": result.OutTradeNo},
|
||||
}, &row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if row.Id < utils.NumberOne {
|
||||
return errors.New("order not found")
|
||||
}
|
||||
if row.Status == dao.PayStatusOk {
|
||||
return nil // idempotent
|
||||
}
|
||||
|
||||
expected := yuanToFen(row.PayPrice)
|
||||
if expected > 0 && result.AmountFen > 0 && expected != result.AmountFen {
|
||||
return errors.New("amount mismatch")
|
||||
}
|
||||
|
||||
updates := map[string]any{
|
||||
"status": dao.PayStatusOk,
|
||||
}
|
||||
if result.OutTradeNo != utils.StringEmpty {
|
||||
updates["out_trade_no"] = result.OutTradeNo
|
||||
}
|
||||
_, err = model.OrderModel{}.Init().Edit(modelbase.Params{
|
||||
Eq: map[string]string{"order_sn": result.OutTradeNo},
|
||||
}, updates)
|
||||
// 回调无登录态:先按支付中状态回写;金额校验依赖 order 服务 UpdateOrderDPayStatus 内实现。
|
||||
err := updateOrderPayStatus(
|
||||
l.ctx,
|
||||
l.svcCtx.OrderSvcName,
|
||||
result.OutTradeNo,
|
||||
dao.PayStatusOk,
|
||||
float64(result.AmountFen)/100,
|
||||
result.TransactionID,
|
||||
0,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"lone-services/pkg/utils"
|
||||
orderpb "lone-services/rpc/order/pb"
|
||||
"lone-services/services/payment/internal/svc"
|
||||
)
|
||||
|
||||
func getOrderInfo(ctx context.Context, orderSvcName string, userID int64, orderSn string, typ uint8) (*orderpb.OrderInfoResponse, error) {
|
||||
if orderSvcName == utils.StringEmpty {
|
||||
return nil, fmt.Errorf("order service not configured")
|
||||
}
|
||||
if userID < utils.NumberOne || orderSn == utils.StringEmpty || typ < utils.NumberOne {
|
||||
return nil, fmt.Errorf("invalid get order params")
|
||||
}
|
||||
|
||||
cli, err := svc.GetRpcClient(orderSvcName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := orderpb.NewOrderClient(cli.Conn())
|
||||
info, err := client.GetOrderInfo(ctx, &orderpb.GetOrderInfoRequest{
|
||||
UserId: userID,
|
||||
OrderSn: orderSn,
|
||||
Type: int32(typ),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info == nil || info.Id < utils.NumberOne {
|
||||
return nil, fmt.Errorf("order not found")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func updateOrderPayStatus(ctx context.Context, orderSvcName, orderSn string, status uint8, payPrice float64, remitNo string, typ uint8) error {
|
||||
if orderSvcName == utils.StringEmpty {
|
||||
return fmt.Errorf("order service not configured")
|
||||
}
|
||||
cli, err := svc.GetRpcClient(orderSvcName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := orderpb.NewOrderClient(cli.Conn())
|
||||
_, err = client.UpdateOrderDPayStatus(ctx, &orderpb.UpdateOrderPayStatusRequest{
|
||||
OrderSn: orderSn,
|
||||
Status: int32(status),
|
||||
PayPrice: payPrice,
|
||||
RemitNo: remitNo,
|
||||
Type: int32(typ),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func orderPayStatus(info *orderpb.OrderInfoResponse) uint8 {
|
||||
if info == nil {
|
||||
return 0
|
||||
}
|
||||
return uint8(info.Status)
|
||||
}
|
||||
|
||||
func orderOutTradeNo(info *orderpb.OrderInfoResponse) string {
|
||||
if info == nil {
|
||||
return utils.StringEmpty
|
||||
}
|
||||
if info.OutTradeNo != utils.StringEmpty {
|
||||
return info.OutTradeNo
|
||||
}
|
||||
return info.OrderSn
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"lone-services/pkg/modelbase"
|
||||
pkgpay "lone-services/pkg/payment"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
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"
|
||||
@@ -45,22 +43,23 @@ func (l *PayLogic) Pay(in *payment.PayReq) (*payment.Response, error) {
|
||||
client := resolveClient(in.Client, loginUser.ClientCode)
|
||||
channelCode := resolveChannel(in.Channel)
|
||||
|
||||
order, err := loadPayableOrder(in.OrderSn, loginUser.ID)
|
||||
orderInfo, err := getOrderInfo(l.ctx, l.svcCtx.OrderSvcName, loginUser.ID, in.OrderSn, loginUser.Type)
|
||||
if err != nil {
|
||||
l.Errorf("pay load order: %v", err)
|
||||
return failResponse(utils.ErrorNotFund), nil
|
||||
}
|
||||
if order.Status == dao.PayStatusOk {
|
||||
if orderPayStatus(orderInfo) == 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 {
|
||||
status := orderPayStatus(orderInfo)
|
||||
if status != dao.PayStatusWait && status != dao.PayStatusIn && status != dao.PayStatusFail {
|
||||
return outResponse(utils.Fail, "订单状态不可支付"), nil
|
||||
}
|
||||
|
||||
amountFen := yuanToFen(order.PayPrice)
|
||||
amountFen := yuanToFen(orderInfo.PayPrice)
|
||||
if amountFen <= 0 {
|
||||
return outResponse(utils.Fail, "支付金额无效"), nil
|
||||
}
|
||||
@@ -97,39 +96,17 @@ func (l *PayLogic) Pay(in *payment.PayReq) (*payment.Response, error) {
|
||||
return outResponse(utils.Fail, err.Error()), nil
|
||||
}
|
||||
|
||||
_ = markOrderPaying(in.OrderSn, channelCode, in.OrderSn)
|
||||
_ = updateOrderPayStatus(l.ctx, l.svcCtx.OrderSvcName, in.OrderSn, dao.PayStatusIn, 0, in.OrderSn, loginUser.Type)
|
||||
|
||||
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)
|
||||
err := modelbase.DB().Table(modelbase.Prefix()+"user_credential").
|
||||
Where("user_id = ? AND credential_type = ? AND status = ?",
|
||||
userID, dao.CredentialTypeOpenid, dao.CredentialEnabled).
|
||||
Take(&cred).Error
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -139,21 +116,6 @@ func loadOpenID(userID int64) (string, error) {
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
@@ -45,14 +43,11 @@ func (l *RefundLogic) Refund(in *payment.RefundReq) (*payment.Response, error) {
|
||||
channelCode := resolveChannel(in.Channel)
|
||||
client := resolveClient(utils.StringEmpty, loginUser.ClientCode)
|
||||
|
||||
var row dao.OrderPayRow
|
||||
err := model.OrderModel{}.Init().GetOne(modelbase.Params{
|
||||
Eq: map[string]string{"order_sn": in.OrderSn},
|
||||
}, &row)
|
||||
if err != nil || row.Id < utils.NumberOne {
|
||||
orderInfo, err := getOrderInfo(l.ctx, l.svcCtx.OrderSvcName, loginUser.ID, in.OrderSn, loginUser.Type)
|
||||
if err != nil {
|
||||
return failResponse(utils.ErrorNotFund), nil
|
||||
}
|
||||
if row.Status != dao.PayStatusOk {
|
||||
if orderPayStatus(orderInfo) != dao.PayStatusOk {
|
||||
return outResponse(utils.Fail, "订单未支付,无法退款"), nil
|
||||
}
|
||||
|
||||
@@ -61,7 +56,7 @@ func (l *RefundLogic) Refund(in *payment.RefundReq) (*payment.Response, error) {
|
||||
return failResponse(utils.ErrorParams), nil
|
||||
}
|
||||
refundFen := yuanToFen(refundYuan)
|
||||
totalFen := yuanToFen(row.PayPrice)
|
||||
totalFen := yuanToFen(orderInfo.PayPrice)
|
||||
if refundFen > totalFen {
|
||||
return outResponse(utils.Fail, "退款金额不能大于支付金额"), nil
|
||||
}
|
||||
@@ -71,11 +66,8 @@ func (l *RefundLogic) Refund(in *payment.RefundReq) (*payment.Response, error) {
|
||||
return outResponse(utils.Fail, err.Error()), nil
|
||||
}
|
||||
|
||||
outTradeNo := row.OutTradeNo
|
||||
if outTradeNo == utils.StringEmpty {
|
||||
outTradeNo = row.OrderSn
|
||||
}
|
||||
outRefundNo := fmt.Sprintf("RF%s%d", row.OrderSn, time.Now().UnixNano())
|
||||
outTradeNo := orderOutTradeNo(orderInfo)
|
||||
outRefundNo := fmt.Sprintf("RF%s%d", orderInfo.OrderSn, time.Now().UnixNano())
|
||||
|
||||
result, err := ch.Refund(l.ctx, &pkgpay.RefundRequest{
|
||||
OutTradeNo: outTradeNo,
|
||||
|
||||
Reference in New Issue
Block a user