2023-10-08 15:59:42 +07:00
package order
import (
2024-06-05 00:24:53 +07:00
"database/sql"
2025-03-04 20:36:17 +07:00
errors2 "enaklo-pos-be/internal/common/errors"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
order2 "enaklo-pos-be/internal/constants/order"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository"
"enaklo-pos-be/internal/utils/generator"
2024-06-04 02:59:31 +07:00
"encoding/json"
"errors"
2024-06-05 00:24:53 +07:00
"fmt"
2023-10-08 15:59:42 +07:00
"go.uber.org/zap"
2024-06-04 02:59:31 +07:00
"golang.org/x/net/context"
"gorm.io/gorm"
"strconv"
"time"
2023-10-08 15:59:42 +07:00
)
2024-08-09 10:02:05 +07:00
type Config interface {
2024-08-21 22:48:14 +07:00
GetOrderFee ( source string ) float64
2024-08-09 10:02:05 +07:00
}
2023-10-08 15:59:42 +07:00
type OrderService struct {
2024-08-28 00:05:47 +07:00
repo repository . Order
crypt repository . Crypto
product repository . Product
2024-10-15 11:52:34 +07:00
pg repository . PaymentGateway
2024-08-28 00:05:47 +07:00
payment repository . Payment
transaction repository . TransactionRepository
txmanager repository . TransactionManager
wallet repository . WalletRepository
2024-10-27 19:48:10 +07:00
linkquRepo repository . LinkQu
2024-08-28 00:05:47 +07:00
cfg Config
2023-10-08 15:59:42 +07:00
}
2024-06-05 00:24:53 +07:00
func NewOrderService (
repo repository . Order ,
product repository . Product , crypt repository . Crypto ,
2024-10-15 11:52:34 +07:00
pg repository . PaymentGateway , payment repository . Payment ,
2024-06-05 00:24:53 +07:00
txmanager repository . TransactionManager ,
2024-08-28 00:05:47 +07:00
wallet repository . WalletRepository , cfg Config ,
transaction repository . TransactionRepository ,
2024-10-27 19:48:10 +07:00
linkquRepo repository . LinkQu ,
2024-08-28 00:05:47 +07:00
) * OrderService {
2023-10-08 15:59:42 +07:00
return & OrderService {
2024-08-28 00:05:47 +07:00
repo : repo ,
product : product ,
crypt : crypt ,
2024-10-15 11:52:34 +07:00
pg : pg ,
2024-08-28 00:05:47 +07:00
payment : payment ,
txmanager : txmanager ,
wallet : wallet ,
cfg : cfg ,
transaction : transaction ,
2024-10-27 19:48:10 +07:00
linkquRepo : linkquRepo ,
2023-10-08 15:59:42 +07:00
}
}
2024-07-30 22:02:36 +07:00
func ( s * OrderService ) CreateOrder ( ctx mycontext . Context , req * entity . OrderRequest ) ( * entity . OrderResponse , error ) {
2024-10-15 11:52:34 +07:00
productIDs , filteredItems := s . filterOrderItems ( req . OrderItems )
if len ( productIDs ) == 0 {
return nil , errors2 . ErrorBadRequest
2024-08-21 22:48:14 +07:00
}
2024-08-22 13:28:39 +07:00
req . OrderItems = filteredItems
2024-08-21 22:48:14 +07:00
if len ( productIDs ) < 1 {
return nil , errors2 . ErrorBadRequest
2024-06-04 02:59:31 +07:00
}
2023-10-08 15:59:42 +07:00
2024-06-04 02:59:31 +07:00
products , err := s . product . GetProductsByIDs ( ctx , productIDs , req . PartnerID )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when getting products by IDs" , zap . Error ( err ) )
return nil , err
2023-10-08 15:59:42 +07:00
}
2024-08-18 12:38:29 +07:00
var siteID int64
2024-06-04 02:59:31 +07:00
productMap := make ( map [ int64 ] * entity . ProductDB )
for _ , product := range products {
productMap [ product . ID ] = product
2024-08-18 12:38:29 +07:00
siteID = product . SiteID
2024-06-04 02:59:31 +07:00
}
2023-10-08 15:59:42 +07:00
2024-06-04 02:59:31 +07:00
totalAmount := 0.0
for _ , item := range req . OrderItems {
product , ok := productMap [ item . ProductID ]
if ! ok {
logger . ContextLogger ( ctx ) . Error ( "product not found" , zap . Int64 ( "productID" , item . ProductID ) )
return nil , errors . New ( "product not found" )
}
totalAmount += product . Price * float64 ( item . Quantity )
}
2023-10-08 15:59:42 +07:00
2024-08-15 22:17:28 +07:00
parsedTime , err := time . Parse ( "2006-01-02" , req . VisitDate )
if err != nil {
fmt . Println ( "Error parsing date:" , err )
2024-08-16 17:34:53 +07:00
return nil , errors . New ( "visit date not defined" )
2024-08-15 22:17:28 +07:00
}
2024-10-15 11:52:34 +07:00
metadata , err := json . Marshal ( map [ string ] string {
"bank_code" : req . BankCode ,
"bank_name" : req . BankName ,
} )
2024-06-04 02:59:31 +07:00
order := & entity . Order {
2024-08-18 12:38:29 +07:00
PartnerID : req . PartnerID ,
RefID : generator . GenerateUUID ( ) ,
Status : order2 . New . String ( ) ,
Amount : totalAmount ,
2024-08-21 22:48:14 +07:00
Total : totalAmount + s . cfg . GetOrderFee ( req . Source ) ,
Fee : s . cfg . GetOrderFee ( req . Source ) ,
2024-08-18 12:38:29 +07:00
PaymentType : req . PaymentMethod ,
SiteID : & siteID ,
CreatedBy : req . CreatedBy ,
OrderItems : [ ] entity . OrderItem { } ,
Source : req . Source ,
VisitDate : parsedTime ,
TicketStatus : "UNUSED" ,
2024-10-15 11:52:34 +07:00
Metadata : metadata ,
2023-10-08 15:59:42 +07:00
}
2024-06-04 02:59:31 +07:00
for _ , item := range req . OrderItems {
order . OrderItems = append ( order . OrderItems , entity . OrderItem {
ItemID : item . ProductID ,
ItemType : productMap [ item . ProductID ] . Type ,
Price : productMap [ item . ProductID ] . Price ,
Quantity : item . Quantity ,
CreatedBy : req . CreatedBy ,
2024-06-05 00:24:53 +07:00
Product : productMap [ item . ProductID ] . ToProduct ( ) ,
2024-06-04 02:59:31 +07:00
} )
}
2023-10-08 15:59:42 +07:00
2024-06-04 02:59:31 +07:00
order , err = s . repo . Create ( ctx , order )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when creating order" , zap . Error ( err ) )
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-04 02:59:31 +07:00
token , err := s . crypt . GenerateJWTOrder ( order )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when create token" , zap . Error ( err ) )
return nil , err
2023-10-08 15:59:42 +07:00
}
2024-08-22 01:21:26 +07:00
order , err = s . repo . FindByID ( ctx , order . ID )
2024-08-21 22:56:53 +07:00
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when creating order" , zap . Error ( err ) )
return nil , err
}
2024-06-04 02:59:31 +07:00
return & entity . OrderResponse {
Order : order ,
Token : token ,
} , nil
2023-10-08 15:59:42 +07:00
}
2024-10-15 11:52:34 +07:00
func ( s * OrderService ) filterOrderItems ( items [ ] entity . OrderItemRequest ) ( [ ] int64 , [ ] entity . OrderItemRequest ) {
var productIDs [ ] int64
var filteredItems [ ] entity . OrderItemRequest
for _ , item := range items {
if item . Quantity != 0 {
productIDs = append ( productIDs , item . ProductID )
filteredItems = append ( filteredItems , item )
}
}
return productIDs , filteredItems
}
2024-08-13 23:09:05 +07:00
func ( s * OrderService ) CheckInInquiry ( ctx mycontext . Context , qrCode string , partnerID * int64 ) ( * entity . CheckinResponse , error ) {
order , err := s . repo . FindByQRCode ( ctx , qrCode )
if err != nil {
2024-08-14 00:10:53 +07:00
if errors . Is ( err , gorm . ErrRecordNotFound ) {
return nil , errors2 . NewErrorMessage ( errors2 . ErrorInvalidRequest , "Not Valid QR Code" )
}
2024-08-13 23:09:05 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when getting order by QR code" , zap . Error ( err ) )
return nil , err
}
if order . PartnerID != * partnerID {
return nil , errors2 . ErrorBadRequest
}
2024-08-13 23:53:01 +07:00
if order . Status != "PAID" {
return nil , errors2 . ErrorInvalidRequest
}
2024-08-27 23:02:33 +07:00
location , _ := time . LoadLocation ( "Asia/Jakarta" )
today := time . Now ( ) . In ( location ) . Format ( "2006-01-02" )
visitDate := time . Date (
order . VisitDate . Year ( ) ,
order . VisitDate . Month ( ) ,
order . VisitDate . Day ( ) ,
0 , 0 , 0 , 0 ,
location ,
) . Format ( "2006-01-02" )
2024-08-13 23:09:05 +07:00
2024-08-27 23:02:33 +07:00
if order . TicketStatus == "USED" || visitDate < today {
return nil , errors2 . NewErrorMessage ( errors2 . ErrorTicketInvalidOrAlreadyUsed ,
"Maaf! Tiket ini tidak valid karena sudah terpakai atau sudah kadaluwarsa" )
}
2024-08-13 23:09:05 +07:00
if visitDate != today {
2024-08-27 23:02:33 +07:00
return nil , errors2 . NewErrorMessage ( errors2 . ErrorTicketInvalidOrAlreadyUsed ,
"Maaf Tiket ini tidak valid karena tidak sesuai dengan tanggal tiket" )
2024-08-13 23:09:05 +07:00
}
token , err := s . crypt . GenerateJWTOrder ( order )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when generate checkin token" , zap . Error ( err ) )
return nil , err
}
orderResponse := & entity . CheckinResponse {
Token : token ,
}
return orderResponse , nil
}
func ( s * OrderService ) CheckInExecute ( ctx mycontext . Context ,
token string , partnerID * int64 ) ( * entity . CheckinExecute , error ) {
pID , orderID , err := s . crypt . ValidateJWTOrder ( token )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when validating JWT order" , zap . Error ( err ) )
return nil , err
}
if pID != * partnerID {
return nil , errors2 . ErrorBadRequest
}
order , err := s . repo . FindByID ( ctx , orderID )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when getting order by ID" , zap . Error ( err ) )
return nil , err
}
resp := & entity . CheckinExecute {
Order : order ,
}
2024-08-13 23:53:01 +07:00
if order . TicketStatus != "UNUSED" {
2024-08-13 23:09:05 +07:00
return resp , nil
}
2024-08-13 23:53:01 +07:00
order . TicketStatus = "USED"
2024-08-13 23:09:05 +07:00
order , err = s . repo . Update ( ctx , order )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when updating order status" , zap . Error ( err ) )
return nil , err
}
return resp , nil
}
2024-10-15 11:52:34 +07:00
func ( s * OrderService ) Execute ( ctx mycontext . Context , req * entity . OrderExecuteRequest ) ( * entity . ExecuteOrderResponse , error ) {
2024-06-04 02:59:31 +07:00
partnerID , orderID , err := s . crypt . ValidateJWTOrder ( req . Token )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when validating JWT order" , zap . Error ( err ) )
return nil , err
2023-10-08 15:59:42 +07:00
}
2024-06-04 02:59:31 +07:00
order , err := s . repo . FindByID ( ctx , orderID )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
if errors . Is ( err , gorm . ErrRecordNotFound ) {
logger . ContextLogger ( ctx ) . Error ( "order not found" , zap . Int64 ( "orderID" , orderID ) )
return nil , errors . New ( "order not found" )
}
logger . ContextLogger ( ctx ) . Error ( "error when finding order by ID" , zap . Error ( err ) )
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-04 02:59:31 +07:00
payment , err := s . payment . FindByOrderAndPartnerID ( ctx , orderID , partnerID )
if err != nil && ! errors . Is ( err , gorm . ErrRecordNotFound ) {
logger . ContextLogger ( ctx ) . Error ( "error getting payment data from db" , zap . Error ( err ) )
return nil , err
}
2023-10-08 15:59:42 +07:00
2024-06-04 02:59:31 +07:00
if payment != nil {
return s . createExecuteOrderResponse ( order , payment ) , nil
}
if order . PartnerID != partnerID {
logger . ContextLogger ( ctx ) . Error ( "partner ID mismatch" , zap . Int64 ( "orderID" , orderID ) , zap . Int64 ( "tokenPartnerID" , partnerID ) , zap . Int64 ( "orderPartnerID" , order . PartnerID ) )
return nil , errors . New ( "partner ID mismatch" )
}
if order . Status != "NEW" {
return nil , errors . New ( "invalid state" )
}
resp := & entity . ExecuteOrderResponse {
Order : order ,
}
if order . PaymentType != "CASH" {
2024-10-15 11:52:34 +07:00
if order . PaymentType == "VA" {
paymentResponse , err := s . processVAPayment ( ctx , order , partnerID , req . CreatedBy )
if err != nil {
return nil , err
}
resp . VirtualAccount = paymentResponse . VirtualAccountNumber
resp . BankName = paymentResponse . BankName
resp . BankCode = paymentResponse . BankCode
}
2024-08-06 16:21:55 +07:00
if order . PaymentType == "QRIS" {
paymentResponse , err := s . processQRPayment ( ctx , order , partnerID , req . CreatedBy )
if err != nil {
return nil , err
}
2024-10-15 11:52:34 +07:00
resp . QRCode = paymentResponse . QRCodeURL
2024-08-06 16:21:55 +07:00
} else {
paymentResponse , err := s . processNonCashPayment ( ctx , order , partnerID , req . CreatedBy )
if err != nil {
return nil , err
}
resp . PaymentToken = paymentResponse . Token
resp . RedirectURL = paymentResponse . RedirectURL
2024-06-04 02:59:31 +07:00
}
}
order . SetExecutePaymentStatus ( )
order , err = s . repo . Update ( ctx , order )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when updating order status" , zap . Error ( err ) )
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-04 02:59:31 +07:00
return resp , nil
2023-10-08 15:59:42 +07:00
}
2024-06-04 02:59:31 +07:00
func ( s * OrderService ) createExecuteOrderResponse ( order * entity . Order , payment * entity . Payment ) * entity . ExecuteOrderResponse {
var metadata map [ string ] string
if err := json . Unmarshal ( payment . RequestMetadata , & metadata ) ; err != nil {
logger . ContextLogger ( context . Background ( ) ) . Error ( "error unmarshaling request metadata" , zap . Error ( err ) )
return & entity . ExecuteOrderResponse {
Order : order ,
}
}
return & entity . ExecuteOrderResponse {
Order : order ,
PaymentToken : metadata [ "payment_token" ] ,
RedirectURL : metadata [ "payment_redirect_url" ] ,
}
}
func ( s * OrderService ) processNonCashPayment ( ctx context . Context , order * entity . Order , partnerID , createdBy int64 ) ( * entity . MidtransResponse , error ) {
2024-10-15 11:52:34 +07:00
paymentRequest := entity . PaymentRequest {
2024-06-04 02:59:31 +07:00
PaymentReferenceID : generator . GenerateUUIDV4 ( ) ,
2024-08-27 19:32:48 +07:00
TotalAmount : int64 ( order . Total ) ,
2024-10-15 11:52:34 +07:00
//OrderItems: order.OrderItems,
Provider : order . PaymentType ,
2024-06-04 02:59:31 +07:00
}
2024-10-15 11:52:34 +07:00
paymentResponse , err := s . pg . CreatePayment ( paymentRequest )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when creating payment" , zap . Error ( err ) )
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-04 02:59:31 +07:00
requestMetadata , err := json . Marshal ( map [ string ] string {
"partner_id" : strconv . FormatInt ( partnerID , 10 ) ,
"created_by" : strconv . FormatInt ( createdBy , 10 ) ,
"payment_token" : paymentResponse . Token ,
"payment_redirect_url" : paymentResponse . RedirectURL ,
} )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when marshaling request metadata" , zap . Error ( err ) )
return nil , err
2023-10-08 15:59:42 +07:00
}
2024-06-04 02:59:31 +07:00
payment := & entity . Payment {
2024-06-05 00:24:53 +07:00
PartnerID : partnerID ,
OrderID : order . ID ,
2024-06-04 02:59:31 +07:00
ReferenceID : paymentRequest . PaymentReferenceID ,
2024-08-06 16:21:55 +07:00
Channel : "MIDTRANS" ,
PaymentType : order . PaymentType ,
Amount : order . Amount ,
State : "PENDING" ,
CreatedAt : time . Now ( ) ,
UpdatedAt : time . Now ( ) ,
RequestMetadata : requestMetadata ,
}
_ , err = s . payment . Create ( ctx , payment )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when creating payment record" , zap . Error ( err ) )
return nil , err
}
2024-10-15 11:52:34 +07:00
return & entity . MidtransResponse {
Token : paymentResponse . Token ,
RedirectURL : paymentResponse . RedirectURL ,
} , nil
2024-08-06 16:21:55 +07:00
}
2024-10-15 11:52:34 +07:00
func ( s * OrderService ) processQRPayment ( ctx mycontext . Context , order * entity . Order , partnerID , createdBy int64 ) ( * entity . PaymentResponse , error ) {
paymentRequest := entity . PaymentRequest {
2024-08-06 16:21:55 +07:00
PaymentReferenceID : generator . GenerateUUIDV4 ( ) ,
2024-10-15 11:52:34 +07:00
TotalAmount : int64 ( order . Total ) ,
Provider : "LINKQU" ,
CustomerID : fmt . Sprintf ( "POS-%d" , ctx . RequestedBy ( ) ) ,
CustomerName : fmt . Sprintf ( "POS-%s" , ctx . GetName ( ) ) ,
2024-08-06 16:21:55 +07:00
}
2024-10-15 11:52:34 +07:00
paymentResponse , err := s . pg . CreateQRISPayment ( paymentRequest )
2024-08-06 16:21:55 +07:00
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when creating payment" , zap . Error ( err ) )
return nil , err
}
requestMetadata , err := json . Marshal ( map [ string ] string {
"partner_id" : strconv . FormatInt ( partnerID , 10 ) ,
"created_by" : strconv . FormatInt ( createdBy , 10 ) ,
2024-10-15 11:52:34 +07:00
"qr_code" : paymentResponse . QRCodeURL ,
2024-08-06 16:21:55 +07:00
} )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when marshaling request metadata" , zap . Error ( err ) )
return nil , err
}
payment := & entity . Payment {
PartnerID : partnerID ,
OrderID : order . ID ,
ReferenceID : paymentRequest . PaymentReferenceID ,
2024-10-15 11:52:34 +07:00
Channel : "LINKQU" ,
PaymentType : order . PaymentType ,
Amount : order . Amount ,
State : "PENDING" ,
CreatedAt : time . Now ( ) ,
UpdatedAt : time . Now ( ) ,
RequestMetadata : requestMetadata ,
}
_ , err = s . payment . Create ( ctx , payment )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when creating payment record" , zap . Error ( err ) )
return nil , err
}
return paymentResponse , nil
}
func ( s * OrderService ) processVAPayment ( ctx mycontext . Context , order * entity . Order , partnerID , createdBy int64 ) ( * entity . PaymentResponse , error ) {
metadata := map [ string ] string { }
if err := json . Unmarshal ( order . Metadata , & metadata ) ; err != nil {
return nil , err
}
paymentRequest := entity . PaymentRequest {
PaymentReferenceID : generator . GenerateUUIDV4 ( ) ,
TotalAmount : int64 ( order . Total ) ,
Provider : "LINKQU" ,
CustomerID : strconv . FormatInt ( order . User . ID , 10 ) ,
CustomerName : order . User . Name ,
CustomerEmail : order . User . Email ,
BankCode : metadata [ "bank_code" ] ,
}
paymentResponse , err := s . pg . CreatePaymentVA ( paymentRequest )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when creating payment" , zap . Error ( err ) )
return nil , err
}
requestMetadata , err := json . Marshal ( map [ string ] string {
"virtual_account" : paymentResponse . VirtualAccountNumber ,
"bank_name" : paymentResponse . BankName ,
"bank_code" : paymentResponse . BankCode ,
} )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when marshaling request metadata" , zap . Error ( err ) )
return nil , err
}
payment := & entity . Payment {
PartnerID : partnerID ,
OrderID : order . ID ,
ReferenceID : paymentRequest . PaymentReferenceID ,
Channel : "LINKQU" ,
2024-06-04 02:59:31 +07:00
PaymentType : order . PaymentType ,
Amount : order . Amount ,
2024-06-05 00:24:53 +07:00
State : "PENDING" ,
2024-06-04 02:59:31 +07:00
CreatedAt : time . Now ( ) ,
UpdatedAt : time . Now ( ) ,
RequestMetadata : requestMetadata ,
2023-10-08 15:59:42 +07:00
}
2024-06-04 02:59:31 +07:00
_ , err = s . payment . Create ( ctx , payment )
2023-10-08 15:59:42 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ) . Error ( "error when creating payment record" , zap . Error ( err ) )
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-04 02:59:31 +07:00
return paymentResponse , nil
2023-10-08 15:59:42 +07:00
}
2024-06-05 00:24:53 +07:00
func ( s * OrderService ) ProcessCallback ( ctx context . Context , req * entity . CallbackRequest ) error {
tx , err := s . txmanager . Begin ( ctx , & sql . TxOptions { Isolation : sql . LevelSerializable } )
if err != nil {
return fmt . Errorf ( "failed to begin transaction: %w" , err )
}
defer tx . Rollback ( )
err = s . processPayment ( ctx , tx , req )
if err != nil {
return fmt . Errorf ( "failed to process payment: %w" , err )
}
return tx . Commit ( ) . Error
}
func ( s * OrderService ) processPayment ( ctx context . Context , tx * gorm . DB , req * entity . CallbackRequest ) error {
existingPayment , err := s . payment . FindByReferenceID ( ctx , tx , req . TransactionID )
if err != nil {
return fmt . Errorf ( "failed to retrieve payment: %w" , err )
}
existingPayment . State = updatePaymentState ( req . TransactionStatus )
_ , err = s . payment . UpdateWithTx ( ctx , tx , existingPayment )
if err != nil {
return fmt . Errorf ( "failed to update payment: %w" , err )
}
2024-08-28 00:05:47 +07:00
order , err := s . repo . FindByID ( ctx , existingPayment . OrderID )
if err != nil {
return fmt . Errorf ( "failed to get order: %w" , err )
}
2024-06-05 00:24:53 +07:00
if err := s . updateOrderStatus ( ctx , tx , existingPayment . State , existingPayment . OrderID ) ; err != nil {
return fmt . Errorf ( "failed to update order status: %w" , err )
}
if existingPayment . State == "PAID" {
if err := s . updateWalletBalance ( ctx , tx , existingPayment . PartnerID , existingPayment . Amount ) ; err != nil {
return fmt . Errorf ( "failed to update wallet balance: %w" , err )
}
2024-08-28 00:05:47 +07:00
transaction := & entity . Transaction {
PartnerID : existingPayment . PartnerID ,
TransactionType : "PAYMENT_RECEIVED" ,
ReferenceID : existingPayment . ReferenceID ,
Status : "SUCCESS" ,
CreatedBy : 0 ,
Amount : existingPayment . Amount ,
Fee : order . Fee ,
Total : order . Total ,
SiteID : order . SiteID ,
}
if _ , err = s . transaction . Create ( ctx , tx , transaction ) ; err != nil {
return fmt . Errorf ( "failed to update transaction: %w" , err )
}
2024-06-05 00:24:53 +07:00
}
return nil
}
func updatePaymentState ( status string ) string {
switch status {
2024-10-27 19:48:10 +07:00
case "settlement" , "capture" , "paid" , "settle" :
2024-06-05 00:24:53 +07:00
return "PAID"
2024-10-27 19:48:10 +07:00
case "expire" , "deny" , "cancel" , "failure" , "EXPIRED" :
2024-06-05 00:24:53 +07:00
return "EXPIRED"
default :
return status
}
}
func ( s * OrderService ) updateOrderStatus ( ctx context . Context , tx * gorm . DB , status string , orderID int64 ) error {
if status != "PENDING" {
return s . repo . SetOrderStatus ( ctx , tx , orderID , status )
}
return nil
}
func ( s * OrderService ) updateWalletBalance ( ctx context . Context , tx * gorm . DB , partnerID int64 , amount float64 ) error {
wallet , err := s . wallet . GetByPartnerID ( ctx , tx , partnerID )
if err != nil {
return fmt . Errorf ( "failed to get wallet: %w" , err )
}
wallet . Balance += amount
_ , err = s . wallet . Update ( ctx , tx , wallet )
return err
}
2024-07-26 11:37:22 +07:00
2024-07-27 16:47:33 +07:00
func ( s * OrderService ) GetAllHistoryOrders ( ctx mycontext . Context , req entity . OrderSearch ) ( [ ] * entity . HistoryOrder , int , error ) {
2024-07-26 22:32:24 +07:00
historyOrders , total , err := s . repo . GetAllHystoryOrders ( ctx , req )
2024-07-26 11:37:22 +07:00
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when get all history orders" , zap . Error ( err ) )
return nil , 0 , err
}
data := historyOrders . ToHistoryOrderList ( )
return data , total , nil
}
2024-07-27 16:47:33 +07:00
2024-08-13 23:09:05 +07:00
func ( s * OrderService ) CountSoldOfTicket ( ctx mycontext . Context , req entity . OrderSearch ) ( * entity . TicketSold , error ) {
2024-07-27 16:47:33 +07:00
ticket , err := s . repo . CountSoldOfTicket ( ctx , req )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when get all history orders" , zap . Error ( err ) )
return nil , err
}
data := ticket . ToTicketSold ( )
return data , nil
}
2024-08-13 23:09:05 +07:00
func ( s * OrderService ) GetDailySales ( ctx mycontext . Context , req entity . OrderSearch ) ( [ ] entity . ProductDailySales , error ) {
2024-08-02 02:27:57 +07:00
dailySales , err := s . repo . GetDailySalesMetrics ( ctx , req )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when get all history orders" , zap . Error ( err ) )
return nil , err
}
return dailySales , nil
}
2024-08-13 23:09:05 +07:00
func ( s * OrderService ) GetPaymentDistribution ( ctx mycontext . Context , req entity . OrderSearch ) ( [ ] entity . PaymentTypeDistribution , error ) {
2024-08-02 15:16:28 +07:00
paymentDistribution , err := s . repo . GetPaymentTypeDistribution ( ctx , req )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when get all history orders" , zap . Error ( err ) )
return nil , err
}
return paymentDistribution , nil
}
2024-08-13 23:09:05 +07:00
func ( s * OrderService ) SumAmount ( ctx mycontext . Context , req entity . OrderSearch ) ( * entity . Order , error ) {
2024-07-27 16:47:33 +07:00
amount , err := s . repo . SumAmount ( ctx , req )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when get amount cash orders" , zap . Error ( err ) )
return nil , err
}
data := amount . ToSumAmount ( )
return data , nil
2024-07-30 22:02:36 +07:00
}
2024-08-04 01:14:59 +07:00
2024-08-06 16:21:55 +07:00
func ( s * OrderService ) GetByID ( ctx mycontext . Context , id int64 , referenceID string ) ( * entity . Order , error ) {
if referenceID != "" {
payment , err := s . payment . FindByReferenceID ( ctx , nil , referenceID )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when getting payment by IDs" , zap . Error ( err ) )
return nil , err
}
id = payment . OrderID
}
2024-08-04 01:14:59 +07:00
order , err := s . repo . FindByID ( ctx , id )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when getting products by IDs" , zap . Error ( err ) )
return nil , err
}
2024-08-06 16:21:55 +07:00
if ctx . IsCasheer ( ) {
return order , nil
2024-08-04 01:14:59 +07:00
}
2024-08-06 16:21:55 +07:00
//if order.CreatedBy != ctx.RequestedBy() {
// return nil, errors2.NewError(errors2.ErrorBadRequest.ErrorType(), "order not found")
//}
2024-08-04 01:14:59 +07:00
return order , nil
}
2024-08-21 15:48:50 +07:00
func ( s * OrderService ) GetPrintDetail ( ctx mycontext . Context , id int64 ) ( * entity . OrderPrintDetail , error ) {
order , err := s . repo . FindPrintDetailByID ( ctx , id )
if err != nil {
logger . ContextLogger ( ctx ) . Error ( "error when getting products by IDs" , zap . Error ( err ) )
return nil , err
}
return order , nil
}
2024-10-27 19:48:10 +07:00
func ( s * OrderService ) ProcessLinkQuCallback ( ctx context . Context , req * entity . LinkQuCallback ) error {
tx , err := s . txmanager . Begin ( ctx , & sql . TxOptions { Isolation : sql . LevelSerializable } )
if err != nil {
return fmt . Errorf ( "failed to begin transaction: %w" , err )
}
defer tx . Rollback ( )
pay , err := s . linkquRepo . CheckPaymentStatus ( req . PaymentReff )
if err != nil {
return fmt . Errorf ( "failed to begin transaction: %w" , err )
}
if pay . ResponseCode != "00" {
return nil
}
err = s . processPayment ( ctx , tx , & entity . CallbackRequest {
TransactionID : req . PartnerReff ,
TransactionStatus : pay . Data . StatusPaid ,
} )
if err != nil {
return fmt . Errorf ( "failed to process payment: %w" , err )
}
return tx . Commit ( ) . Error
}