add order and payment

This commit is contained in:
aditya.siregar
2024-06-04 02:59:31 +07:00
parent 8a23e72230
commit 4ae9079ff9
34 changed files with 810 additions and 1172 deletions
+45
View File
@@ -20,6 +20,8 @@ func NewCrypto(config CryptoConfig) *CryptoImpl {
type CryptoConfig interface {
AccessTokenSecret() string
AccessTokenOrderSecret() string
AccessTokenOrderExpiresDate() time.Time
AccessTokenExpiresDate() time.Time
}
@@ -88,3 +90,46 @@ func (c *CryptoImpl) ParseAndValidateJWT(tokenString string) (*entity.JWTAuthCla
return nil, errors.ErrorUnauthorized
}
}
func (c *CryptoImpl) GenerateJWTOrder(order *entity.Order) (string, error) {
claims := &entity.JWTOrderClaims{
StandardClaims: jwt.StandardClaims{
Subject: strconv.FormatInt(order.ID, 10),
ExpiresAt: c.Config.AccessTokenOrderExpiresDate().Unix(),
IssuedAt: time.Now().Unix(),
NotBefore: time.Now().Unix(),
},
PartnerID: order.PartnerID,
OrderID: order.ID,
}
token, err := jwt.
NewWithClaims(jwt.SigningMethodHS256, claims).
SignedString([]byte(c.Config.AccessTokenOrderSecret()))
if err != nil {
return "", err
}
return token, nil
}
func (c *CryptoImpl) ValidateJWTOrder(tokenString string) (int64, int64, error) {
token, err := jwt.ParseWithClaims(tokenString, &entity.JWTOrderClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(c.Config.AccessTokenOrderSecret()), nil
})
if err != nil {
return 0, 0, err
}
claims, ok := token.Claims.(*entity.JWTOrderClaims)
if !ok || !token.Valid {
return 0, 0, fmt.Errorf("invalid token %v", token.Header["alg"])
}
return claims.PartnerID, claims.OrderID, nil
}
+81
View File
@@ -0,0 +1,81 @@
package mdtrns
import (
"fmt"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"log"
"strconv"
"github.com/veritrans/go-midtrans"
)
type MidtransConfig interface {
MidtransServerKey() string
MidtransClientKey() string
MidtranEnvType() int
}
type ClientService struct {
client midtrans.Client
midtransConfig MidtransConfig
}
func New(midtransConfig MidtransConfig) *ClientService {
midclient := midtrans.NewClient()
midclient.ServerKey = midtransConfig.MidtransServerKey()
midclient.ClientKey = midtransConfig.MidtransClientKey()
midclient.APIEnvType = midtrans.EnvironmentType(midtransConfig.MidtranEnvType())
return &ClientService{
client: midclient,
midtransConfig: midtransConfig,
}
}
func (c *ClientService) CreatePayment(order entity.MidtransRequest) (*entity.MidtransResponse, error) {
var snapGateway midtrans.SnapGateway
snapGateway = midtrans.SnapGateway{
Client: c.client,
}
snapReq := &midtrans.SnapReq{
EnabledPayments: []midtrans.PaymentType{
midtrans.SourceGopay,
},
TransactionDetails: midtrans.TransactionDetails{
OrderID: order.PaymentReferenceID,
GrossAmt: order.TotalAmount,
},
}
log.Println("GetToken:")
snapTokenResp, err := snapGateway.GetToken(snapReq)
if err != nil {
logger.GetLogger().Error(fmt.Sprintf("error when create midtrans payment %v", err))
return nil, err
}
return &entity.MidtransResponse{
Token: snapTokenResp.Token,
RedirectURL: snapTokenResp.RedirectURL,
}, nil
}
func (c ClientService) getProductItems(products []entity.OrderItem) []midtrans.ItemDetail {
var items []midtrans.ItemDetail
for _, product := range products {
item := midtrans.ItemDetail{
ID: strconv.FormatInt(product.ID, 10),
Name: product.ItemType,
Qty: int32(product.Quantity),
Price: int64(product.Price),
}
items = append(items, item)
}
return items
}
+22 -266
View File
@@ -2,10 +2,8 @@ package orders
import (
"context"
"fmt"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -20,284 +18,42 @@ func NewOrderRepository(db *gorm.DB) *OrderRepository {
}
}
func (o *OrderRepository) CreateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error) {
tx := o.db.Begin()
if err := tx.Select("branch_id", "status", "customer_name", "customer_phone", "pax", "amount", "created_by").Create(order).Error; err != nil {
tx.Rollback()
logError(ctx, "creating order", err)
func (r *OrderRepository) Create(ctx context.Context, order *entity.Order) (*entity.Order, error) {
err := r.db.WithContext(ctx).Create(order).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when creating order", zap.Error(err))
return nil, err
}
for i, orditem := range order.OrderItem {
orderItem := orditem.ToOrderItemDB()
orderItem.OrderID = order.ID
err := tx.Select("order_id", "item_id", "item_type", "price", "qty", "created_by").Create(orderItem).Error
if err != nil {
logger.ContextLogger(ctx).Error("error when create order item", zap.Error(err))
return nil, err
}
order.OrderItem[i] = *orderItem.ToOrderItem()
}
//insert transaction
transaction := order.Transaction.ToTransactionDB()
transaction.OrderID = order.ID
if err := tx.Select("branch_id", "status", "amount", "order_id", "payment_method", "customer_name", "customer_phone", "created_by").Create(transaction).Error; err != nil {
tx.Rollback()
logError(ctx, "creating transaction", err)
return nil, err
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
logError(ctx, "committing transaction", err)
return nil, err
}
return order, nil
}
func (b *OrderRepository) UpdateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error) {
if err := b.db.Select("status", "updated_at", "updated_by").Save(order).Error; err != nil {
logError(ctx, "update order", err)
func (r *OrderRepository) UpdateStatus(ctx context.Context, orderID int64, status string) (*entity.Order, error) {
order := new(entity.Order)
if err := r.db.WithContext(ctx).First(order, orderID).Error; err != nil {
logger.ContextLogger(ctx).Error("error when finding order", zap.Error(err))
return nil, err
}
order.Status = status
if err := r.db.WithContext(ctx).Save(order).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating order status", zap.Error(err))
return nil, err
}
return order, nil
}
func (b *OrderRepository) GetAllOrders(ctx context.Context, req entity.OrderSearch) (entity.OrderList, int, error) {
var orders []*entity.OrderDB
var total int64
query := b.db.Table("orders").
Select("orders.id, orders.branch_id, b.name as branch_name, orders.status, orders.amount, orders.created_at, orders.updated_at, oi.order_item_id, oi.order_id, oi.item_id, oi.item_type, COALESCE(p.name, s.name, '') as item_name, oi.price, oi.qty, oi.created_at, oi.updated_at, COALESCE(t.payment_method, ''), COALESCE(orders.customer_name, ''), COALESCE(orders.customer_phone, ''), COALESCE(orders.pax, 0)").
Joins("LEFT JOIN order_items oi ON orders.id = oi.order_id").
Joins("LEFT JOIN transactions t ON orders.id = t.order_id").
Joins("LEFT JOIN products p ON oi.item_id = p.id AND oi.item_type ='PRODUCT' ").
Joins("LEFT JOIN studios s ON oi.item_id = s.id AND oi.item_type ='STUDIO' ").
Joins("LEFT JOIN branches b ON orders.branch_id = b.id")
if req.Search != "" {
query = query.Where("b.name ILIKE ? or orders.status ILIKE ? or oi.item_type ILIKE ? or p.name ILIKE ? or orders.customer_name ILIKE ? ", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%", "%"+req.Search+"%")
}
if req.Status != "" {
query = query.Where("orders.status = ?", req.Status)
}
if req.BranchID > 0 {
query = query.Where("orders.branch_id = ?", req.BranchID)
}
if req.StatusActive.IsActive() {
query = query.Joins("INNER JOIN (SELECT o.id, oi.qty, o.created_at FROM orders o INNER JOIN order_items oi ON o.id = oi.order_id AND oi.item_type = 'STUDIO' where o.status != 'CANCEL' and CURRENT_TIMESTAMP > o.created_at AND CURRENT_TIMESTAMP < (o.created_at + (oi.qty || ' hours')::interval)) order_active on order_active.id=orders.id")
}
if req.Limit > 0 {
query = query.Limit(req.Limit)
}
if req.Offset > 0 {
query = query.Offset(req.Offset)
}
query.Order("orders.created_at DESC")
rows, err := query.Rows()
if err != nil {
return nil, 0, err
}
defer rows.Close()
ordersMap := make(map[int64]*entity.OrderDB) // Map to store orders by ID
for rows.Next() {
var ordr entity.OrderDB
var oi entity.OrderItem
err := rows.Scan(&ordr.ID, &ordr.BranchID, &ordr.BranchName, &ordr.Status, &ordr.Amount, &ordr.CreatedAt, &ordr.UpdatedAt,
&oi.OrderItemID, &oi.OrderID, &oi.ItemID, &oi.ItemType, &oi.ItemName, &oi.Price, &oi.Qty, &oi.CreatedAt, &oi.UpdatedAt,
&ordr.Transaction.PaymentMethod, &ordr.CustomerName, &ordr.CustomerPhone, &ordr.Pax)
if err != nil {
logger.ContextLogger(ctx).Error("error scanning rows", zap.Error(err))
return nil, 0, err
}
if order, ok := ordersMap[ordr.ID]; ok {
// Order already exists in map, append OrderItem to existing order
order.OrderItem = append(order.OrderItem, oi)
} else {
// Order doesn't exist in map, create a new OrderDB
newOrder := ordr
newOrder.OrderItem = []entity.OrderItem{oi}
ordersMap[ordr.ID] = &newOrder
orders = append(orders, &ordr)
}
}
// assign value order item
for _, v := range orders {
v.OrderItem = ordersMap[v.ID].OrderItem
}
//reset limit for count total data
query = query.Offset(-1).Limit(-1)
if err := query.Count(&total).Error; err != nil {
logger.ContextLogger(ctx).Error("error when count orders", zap.Error(err))
return nil, 0, err
}
return orders, int(total), nil
}
func (b *OrderRepository) GetOrderByID(ctx context.Context, id int64) (*entity.OrderDB, error) {
var orders *entity.OrderDB
query := b.db.Table("orders").
Select("orders.id, orders.branch_id, b.name as branch_name, orders.status, orders.amount, orders.created_at, orders.updated_at, oi.order_item_id, oi.order_id, oi.item_id, oi.item_type, COALESCE(p.name, s.name, '') as item_name, oi.price, oi.qty, oi.created_at, oi.updated_at, t.payment_method, orders.customer_name, orders.customer_phone, orders.pax").
Joins("LEFT JOIN order_items oi ON orders.id = oi.order_id").
Joins("LEFT JOIN transactions t ON orders.id = t.order_id").
Joins("LEFT JOIN products p ON oi.item_id = p.id AND oi.item_type ='PRODUCT' ").
Joins("LEFT JOIN studios s ON oi.item_id = s.id AND oi.item_type ='STUDIO' ").
Joins("LEFT JOIN branches b ON orders.branch_id = b.id").
Where("orders.id = ?", id)
rows, err := query.Rows()
if err != nil {
func (r *OrderRepository) FindByID(ctx context.Context, id int64) (*entity.Order, error) {
var order entity.Order
if err := r.db.WithContext(ctx).Preload("OrderItems").First(&order, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when finding order by ID", zap.Error(err))
return nil, err
}
defer rows.Close()
var ordr entity.OrderDB // Map to store orders by ID
for rows.Next() {
var oi entity.OrderItem
err := rows.Scan(&ordr.ID, &ordr.BranchID, &ordr.BranchName, &ordr.Status, &ordr.Amount, &ordr.CreatedAt, &ordr.UpdatedAt,
&oi.OrderItemID, &oi.OrderID, &oi.ItemID, &oi.ItemType, &oi.ItemName, &oi.Price, &oi.Qty, &oi.CreatedAt, &oi.UpdatedAt,
&ordr.Transaction.PaymentMethod, &ordr.CustomerName, &ordr.CustomerPhone, &ordr.Pax)
if err != nil {
logger.ContextLogger(ctx).Error("error scanning rows", zap.Error(err))
return nil, err
}
ordr.OrderItem = append(ordr.OrderItem, oi)
}
orders = &ordr
if orders == nil {
return nil, fmt.Errorf("order not found")
}
return orders, nil
return &order, nil
}
func (b *OrderRepository) GetTotalRevenue(ctx context.Context, req entity.OrderTotalRevenueSearch) (float64, int64, error) {
var (
totalmonthlyRevenue float64
totalmonthlyTrans int64
)
query := b.db.Table("orders").
Select("COALESCE(sum(amount),0) as total_amount, COALESCE(count(id),0) as total_transaction").
Where("status in ('NEW','PAID') ")
if req.BranchID > 0 {
query = query.Where("branch_id = ?", req.BranchID)
}
if req.Month > 0 {
query = query.Where("EXTRACT(MONTH FROM created_at) = ? ", req.Month)
}
if req.Year > 0 {
query = query.Where("EXTRACT(YEAR FROM created_at) = ? ", req.Year)
}
if req.DateStart != nil {
query = query.Where("created_at >= ? ", req.DateStart)
}
if req.DateEnd != nil {
query = query.Where("created_at <= ? ", req.DateEnd)
}
rows, err := query.Rows()
if err != nil {
return totalmonthlyRevenue, totalmonthlyTrans, err
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&totalmonthlyRevenue, &totalmonthlyTrans)
if err != nil {
logger.ContextLogger(ctx).Error("error scanning rows", zap.Error(err))
return totalmonthlyRevenue, totalmonthlyTrans, err
}
}
return totalmonthlyRevenue, totalmonthlyTrans, nil
}
func (b *OrderRepository) GetYearlyRevenue(ctx context.Context, year int) (entity.OrderYearlyRevenueList, error) {
var result entity.OrderYearlyRevenueList
err := b.db.Raw(` SELECT
oi.item_type,
EXTRACT(MONTH FROM o.created_at) AS month_number,
SUM(oi.price ) AS total_amount
FROM
orders o
JOIN
order_items oi ON o.id = oi.order_id
WHERE
EXTRACT(YEAR FROM o.created_at) = ?
AND o.status IN ('NEW', 'PAID')
GROUP BY
EXTRACT(MONTH FROM o.created_at),
oi.item_type
ORDER BY
month_number,
oi.item_type`, year).Scan(&result).Error
return result, err
}
func (b *OrderRepository) GetBranchRevenue(ctx context.Context, req entity.OrderBranchRevenueSearch) (entity.OrderBranchRevenueList, error) {
var result entity.OrderBranchRevenueList
query := b.db.Table("orders o").
Joins("JOIN branches ON branches.id = o.branch_id").
Select("o.branch_id, branches.name, branches.location, SUM(o.amount) as total_amount, COUNT(o.id) as total_trans").
Where("o.status IN ('NEW', 'PAID')").
Group("o.branch_id, branches.name, branches.location").
Order("total_amount DESC, total_trans DESC")
if req.DateStart != nil {
query = query.Where("o.created_at >= ? ", req.DateStart)
}
if req.DateEnd != nil {
query = query.Where("o.created_at <= ? ", req.DateEnd)
}
if err := query.Find(&result).Error; err != nil {
logger.ContextLogger(ctx).Error("error when GetBranchRevenue", zap.Error(err))
func (r *OrderRepository) Update(ctx context.Context, order *entity.Order) (*entity.Order, error) {
if err := r.db.WithContext(ctx).Save(order).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating order", zap.Error(err))
return nil, err
}
return result, nil
}
func logError(ctx context.Context, s string, err error) {
panic("unimplemented")
return order, nil
}
+72
View File
@@ -0,0 +1,72 @@
package payment
import (
"context"
"furtuna-be/internal/common/logger"
"furtuna-be/internal/entity"
"github.com/google/uuid"
"go.uber.org/zap"
"gorm.io/gorm"
"strconv"
)
type PaymentRepository struct {
db *gorm.DB
}
func NewPaymentRepository(db *gorm.DB) *PaymentRepository {
return &PaymentRepository{
db: db,
}
}
func (r *PaymentRepository) Create(ctx context.Context, payment *entity.Payment) (*entity.Payment, error) {
payment.ID = uuid.New()
if err := r.db.WithContext(ctx).Create(payment).Error; err != nil {
logger.ContextLogger(ctx).Error("error when creating payment", zap.Error(err))
return nil, err
}
return payment, nil
}
// Update updates an existing payment record in the database
func (r *PaymentRepository) Update(ctx context.Context, payment *entity.Payment) (*entity.Payment, error) {
if err := r.db.WithContext(ctx).Save(payment).Error; err != nil {
logger.ContextLogger(ctx).Error("error when updating payment", zap.Error(err))
return nil, err
}
return payment, nil
}
// FindByID retrieves a payment record by its ID
func (r *PaymentRepository) FindByID(ctx context.Context, id uuid.UUID) (*entity.Payment, error) {
payment := new(entity.Payment)
if err := r.db.WithContext(ctx).First(payment, id).Error; err != nil {
logger.ContextLogger(ctx).Error("error when finding payment by ID", zap.Error(err))
return nil, err
}
return payment, nil
}
func (r *PaymentRepository) FindByOrderAndPartnerID(ctx context.Context, orderID, partnerID int64) (*entity.Payment, error) {
payment := new(entity.Payment)
orderIDStr := strconv.FormatInt(orderID, 10)
partnerIDStr := strconv.FormatInt(partnerID, 10)
if err := r.db.WithContext(ctx).
Where("order_id = ? AND partner_id = ?", orderIDStr, partnerIDStr).
First(payment).Error; err != nil {
logger.ContextLogger(ctx).Error("error when finding payment by order ID and partner ID", zap.Error(err))
return nil, err
}
return payment, nil
}
// FindByReferenceID retrieves a payment record by its reference ID
func (r *PaymentRepository) FindByReferenceID(ctx context.Context, referenceID string) (*entity.Payment, error) {
payment := new(entity.Payment)
if err := r.db.WithContext(ctx).Where("reference_id = ?", referenceID).First(payment).Error; err != nil {
logger.ContextLogger(ctx).Error("error when finding payment by reference ID", zap.Error(err))
return nil, err
}
return payment, nil
}
+9
View File
@@ -105,3 +105,12 @@ func (b *ProductRepository) DeleteProduct(ctx context.Context, id int64) error {
}
return nil
}
func (b *ProductRepository) GetProductsByIDs(ctx context.Context, ids []int64, partnerID int64) ([]*entity.ProductDB, error) {
var products []*entity.ProductDB
if err := b.db.Where("partner_id = ? AND id IN ?", partnerID, ids).Find(&products).Error; err != nil {
logger.ContextLogger(ctx).Error("error when getting products by IDs and partner ID", zap.Error(err))
return nil, err
}
return products, nil
}
+48 -33
View File
@@ -4,9 +4,11 @@ import (
"context"
"database/sql"
"furtuna-be/internal/repository/branches"
mdtrns "furtuna-be/internal/repository/midtrans"
"furtuna-be/internal/repository/orders"
"furtuna-be/internal/repository/oss"
"furtuna-be/internal/repository/partners"
"furtuna-be/internal/repository/payment"
"furtuna-be/internal/repository/products"
"furtuna-be/internal/repository/sites"
"furtuna-be/internal/repository/studios"
@@ -24,36 +26,40 @@ import (
)
type RepoManagerImpl struct {
Crypto Crypto
Auth Auth
Event Event
User User
Branch Branch
Studio Studio
Product Product
Order Order
OSS OSSRepository
Partner PartnerRepository
Site SiteRepository
Trx TransactionManager
Wallet WalletRepository
Crypto Crypto
Auth Auth
Event Event
User User
Branch Branch
Studio Studio
Product Product
Order Order
OSS OSSRepository
Partner PartnerRepository
Site SiteRepository
Trx TransactionManager
Wallet WalletRepository
Midtrans Midtrans
Payment Payment
}
func NewRepoManagerImpl(db *gorm.DB, cfg *config.Config) *RepoManagerImpl {
return &RepoManagerImpl{
Crypto: crypto.NewCrypto(cfg.Auth()),
Auth: auth.NewAuthRepository(db),
Event: event.NewEventRepo(db),
User: users.NewUserRepository(db),
Branch: branches.NewBranchRepository(db),
Studio: studios.NewStudioRepository(db),
Product: products.NewProductRepository(db),
Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
Partner: partners.NewPartnerRepository(db),
Site: sites.NewSiteRepository(db),
Trx: trx.NewGormTransactionManager(db),
Wallet: repository.NewWalletRepository(db),
Crypto: crypto.NewCrypto(cfg.Auth()),
Auth: auth.NewAuthRepository(db),
Event: event.NewEventRepo(db),
User: users.NewUserRepository(db),
Branch: branches.NewBranchRepository(db),
Studio: studios.NewStudioRepository(db),
Product: products.NewProductRepository(db),
Order: orders.NewOrderRepository(db),
OSS: oss.NewOssRepositoryImpl(cfg.OSSConfig),
Partner: partners.NewPartnerRepository(db),
Site: sites.NewSiteRepository(db),
Trx: trx.NewGormTransactionManager(db),
Wallet: repository.NewWalletRepository(db),
Midtrans: mdtrns.New(&cfg.Midtrans),
Payment: payment.NewPaymentRepository(db),
}
}
@@ -73,6 +79,8 @@ type Crypto interface {
CompareHashAndPassword(hash string, password string) bool
ValidateWT(tokenString string) (*jwt.Token, error)
GenerateJWT(user *entity.User) (string, error)
GenerateJWTOrder(order *entity.Order) (string, error)
ValidateJWTOrder(tokenString string) (int64, int64, error)
ParseAndValidateJWT(token string) (*entity.JWTAuthClaims, error)
}
@@ -106,16 +114,13 @@ type Product interface {
GetProductByID(ctx context.Context, id int64) (*entity.ProductDB, error)
GetAllProducts(ctx context.Context, req entity.ProductSearch) (entity.ProductList, int, error)
DeleteProduct(ctx context.Context, id int64) error
GetProductsByIDs(ctx context.Context, ids []int64, partnerID int64) ([]*entity.ProductDB, error)
}
type Order interface {
CreateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error)
UpdateOrder(ctx context.Context, order *entity.OrderDB) (*entity.OrderDB, error)
GetOrderByID(ctx context.Context, id int64) (*entity.OrderDB, error)
GetAllOrders(ctx context.Context, req entity.OrderSearch) (entity.OrderList, int, error)
GetTotalRevenue(ctx context.Context, req entity.OrderTotalRevenueSearch) (float64, int64, error)
GetYearlyRevenue(ctx context.Context, year int) (entity.OrderYearlyRevenueList, error)
GetBranchRevenue(ctx context.Context, req entity.OrderBranchRevenueSearch) (entity.OrderBranchRevenueList, error)
Create(ctx context.Context, order *entity.Order) (*entity.Order, error)
FindByID(ctx context.Context, id int64) (*entity.Order, error)
Update(ctx context.Context, order *entity.Order) (*entity.Order, error)
}
type OSSRepository interface {
@@ -150,3 +155,13 @@ type TransactionManager interface {
type WalletRepository interface {
Create(ctx context.Context, tx *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error)
}
type Midtrans interface {
CreatePayment(order entity.MidtransRequest) (*entity.MidtransResponse, error)
}
type Payment interface {
Create(ctx context.Context, payment *entity.Payment) (*entity.Payment, error)
Update(ctx context.Context, payment *entity.Payment) (*entity.Payment, error)
FindByOrderAndPartnerID(ctx context.Context, orderID, partnerID int64) (*entity.Payment, error)
}