Merge pull request 'Add QR Payment' (#8) from customer-order into main
Reviewed-on: https://git.altru.id/Furtuna/furtuna-backend/pulls/8
This commit is contained in:
commit
a51563d3f9
@ -11,3 +11,9 @@ type MidtransRequest struct {
|
|||||||
TotalAmount int64
|
TotalAmount int64
|
||||||
OrderItems []OrderItem
|
OrderItems []OrderItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MidtransQrisResponse struct {
|
||||||
|
QrCodeUrl string
|
||||||
|
OrderID string
|
||||||
|
Amount int64
|
||||||
|
}
|
||||||
|
|||||||
@ -45,6 +45,7 @@ type OrderResponse struct {
|
|||||||
|
|
||||||
type ExecuteOrderResponse struct {
|
type ExecuteOrderResponse struct {
|
||||||
Order *Order
|
Order *Order
|
||||||
|
QRCode string
|
||||||
PaymentToken string
|
PaymentToken string
|
||||||
RedirectURL string
|
RedirectURL string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,6 +139,7 @@ func MapOrderToExecuteOrderResponse(orderResponse *entity.ExecuteOrderResponse)
|
|||||||
OrderItems: orderItems,
|
OrderItems: orderItems,
|
||||||
PaymentToken: orderResponse.PaymentToken,
|
PaymentToken: orderResponse.PaymentToken,
|
||||||
RedirectURL: orderResponse.RedirectURL,
|
RedirectURL: orderResponse.RedirectURL,
|
||||||
|
QRcode: orderResponse.QRCode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +200,7 @@ func (h *Handler) Detail(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := request.GetMyContext(c)
|
ctx := request.GetMyContext(c)
|
||||||
order, err := h.service.GetByID(ctx, req.ID)
|
order, err := h.service.GetByID(ctx, req.ID, req.ReferenceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ErrorWrapper(c, err)
|
response.ErrorWrapper(c, err)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -98,9 +98,10 @@ func (e Execute) ToOrderExecuteRequest(createdBy int64) *entity.OrderExecuteRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OrderParamCustomer struct {
|
type OrderParamCustomer struct {
|
||||||
ID int64 `form:"id" json:"id" example:"10"`
|
ID int64 `form:"id" json:"id" example:"10"`
|
||||||
Limit int `form:"limit" json:"limit" example:"10"`
|
ReferenceID string `form:"reference_id" json:"reference_id" example:"10"`
|
||||||
Offset int `form:"offset" json:"offset" example:"0"`
|
Limit int `form:"limit" json:"limit" example:"10"`
|
||||||
|
Offset int `form:"offset" json:"offset" example:"0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OrderParamCustomer) ToOrderEntity(ctx mycontext.Context) entity.OrderSearch {
|
func (o *OrderParamCustomer) ToOrderEntity(ctx mycontext.Context) entity.OrderSearch {
|
||||||
|
|||||||
@ -102,6 +102,7 @@ type ExecuteOrderResponse struct {
|
|||||||
OrderItems []CreateOrderItemResponse `json:"order_items"`
|
OrderItems []CreateOrderItemResponse `json:"order_items"`
|
||||||
PaymentToken string `json:"payment_token"`
|
PaymentToken string `json:"payment_token"`
|
||||||
RedirectURL string `json:"redirect_url"`
|
RedirectURL string `json:"redirect_url"`
|
||||||
|
QRcode string `json:"qr_code"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateOrderItemResponse struct {
|
type CreateOrderItemResponse struct {
|
||||||
|
|||||||
@ -39,10 +39,10 @@ func (c *ClientService) CreatePayment(order entity.MidtransRequest) (*entity.Mid
|
|||||||
Client: c.client,
|
Client: c.client,
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentMethod := []midtrans.PaymentType{}
|
var paymentMethod []midtrans.PaymentType
|
||||||
|
|
||||||
if order.PaymentMethod == "GOPAY" {
|
if order.PaymentMethod == "QRIS" {
|
||||||
paymentMethod = append(paymentMethod, midtrans.SourceGopay)
|
paymentMethod = []midtrans.PaymentType{midtrans.SourceGopay}
|
||||||
}
|
}
|
||||||
|
|
||||||
snapReq := &midtrans.SnapReq{
|
snapReq := &midtrans.SnapReq{
|
||||||
@ -83,3 +83,44 @@ func (c ClientService) getProductItems(products []entity.OrderItem) []midtrans.I
|
|||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ClientService) CreateQrisPayment(order entity.MidtransRequest) (*entity.MidtransQrisResponse, error) {
|
||||||
|
coreGateway := midtrans.CoreGateway{
|
||||||
|
Client: c.client,
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &midtrans.ChargeReq{
|
||||||
|
PaymentType: midtrans.SourceGopay,
|
||||||
|
TransactionDetails: midtrans.TransactionDetails{
|
||||||
|
OrderID: order.PaymentReferenceID,
|
||||||
|
GrossAmt: order.TotalAmount,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request charge and retrieve response
|
||||||
|
resp, err := coreGateway.Charge(req)
|
||||||
|
if err != nil {
|
||||||
|
logger.GetLogger().Error(fmt.Sprintf("error when creating QRIS payment: %v", err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract QR code URL from response actions
|
||||||
|
var qrCodeURL string
|
||||||
|
for _, action := range resp.Actions {
|
||||||
|
if action.Name == "generate-qr-code" {
|
||||||
|
qrCodeURL = action.URL
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if qrCodeURL == "" {
|
||||||
|
logger.GetLogger().Error("error: QR code URL not provided in response")
|
||||||
|
return nil, fmt.Errorf("QR code URL not provided in response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &entity.MidtransQrisResponse{
|
||||||
|
QrCodeUrl: qrCodeURL,
|
||||||
|
OrderID: order.PaymentReferenceID,
|
||||||
|
Amount: order.TotalAmount,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -72,6 +72,10 @@ func (r *PaymentRepository) FindByOrderAndPartnerID(ctx context.Context, orderID
|
|||||||
// FindByReferenceID retrieves a payment record by its reference ID
|
// FindByReferenceID retrieves a payment record by its reference ID
|
||||||
func (r *PaymentRepository) FindByReferenceID(ctx context.Context, db *gorm.DB, referenceID string) (*entity.Payment, error) {
|
func (r *PaymentRepository) FindByReferenceID(ctx context.Context, db *gorm.DB, referenceID string) (*entity.Payment, error) {
|
||||||
payment := new(entity.Payment)
|
payment := new(entity.Payment)
|
||||||
|
if db == nil {
|
||||||
|
db = r.db
|
||||||
|
}
|
||||||
|
|
||||||
if err := db.WithContext(ctx).Where("reference_id = ?", referenceID).First(payment).Error; err != nil {
|
if err := 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))
|
logger.ContextLogger(ctx).Error("error when finding payment by reference ID", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -192,6 +192,7 @@ type WalletRepository interface {
|
|||||||
|
|
||||||
type Midtrans interface {
|
type Midtrans interface {
|
||||||
CreatePayment(order entity.MidtransRequest) (*entity.MidtransResponse, error)
|
CreatePayment(order entity.MidtransRequest) (*entity.MidtransResponse, error)
|
||||||
|
CreateQrisPayment(order entity.MidtransRequest) (*entity.MidtransQrisResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Payment interface {
|
type Payment interface {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
errors2 "furtuna-be/internal/common/errors"
|
|
||||||
"furtuna-be/internal/common/logger"
|
"furtuna-be/internal/common/logger"
|
||||||
"furtuna-be/internal/common/mycontext"
|
"furtuna-be/internal/common/mycontext"
|
||||||
order2 "furtuna-be/internal/constants/order"
|
order2 "furtuna-be/internal/constants/order"
|
||||||
@ -155,12 +154,20 @@ func (s *OrderService) Execute(ctx context.Context, req *entity.OrderExecuteRequ
|
|||||||
}
|
}
|
||||||
|
|
||||||
if order.PaymentType != "CASH" {
|
if order.PaymentType != "CASH" {
|
||||||
paymentResponse, err := s.processNonCashPayment(ctx, order, partnerID, req.CreatedBy)
|
if order.PaymentType == "QRIS" {
|
||||||
if err != nil {
|
paymentResponse, err := s.processQRPayment(ctx, order, partnerID, req.CreatedBy)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.QRCode = paymentResponse.QrCodeUrl
|
||||||
|
} else {
|
||||||
|
paymentResponse, err := s.processNonCashPayment(ctx, order, partnerID, req.CreatedBy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp.PaymentToken = paymentResponse.Token
|
||||||
|
resp.RedirectURL = paymentResponse.RedirectURL
|
||||||
}
|
}
|
||||||
resp.PaymentToken = paymentResponse.Token
|
|
||||||
resp.RedirectURL = paymentResponse.RedirectURL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
order.SetExecutePaymentStatus()
|
order.SetExecutePaymentStatus()
|
||||||
@ -217,7 +224,54 @@ func (s *OrderService) processNonCashPayment(ctx context.Context, order *entity.
|
|||||||
PartnerID: partnerID,
|
PartnerID: partnerID,
|
||||||
OrderID: order.ID,
|
OrderID: order.ID,
|
||||||
ReferenceID: paymentRequest.PaymentReferenceID,
|
ReferenceID: paymentRequest.PaymentReferenceID,
|
||||||
Channel: "XENDIT",
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return paymentResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderService) processQRPayment(ctx context.Context, order *entity.Order, partnerID, createdBy int64) (*entity.MidtransQrisResponse, error) {
|
||||||
|
paymentRequest := entity.MidtransRequest{
|
||||||
|
PaymentReferenceID: generator.GenerateUUIDV4(),
|
||||||
|
TotalAmount: int64(order.Amount),
|
||||||
|
OrderItems: order.OrderItems,
|
||||||
|
PaymentMethod: order.PaymentType,
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentResponse, err := s.midtrans.CreateQrisPayment(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{
|
||||||
|
"partner_id": strconv.FormatInt(partnerID, 10),
|
||||||
|
"created_by": strconv.FormatInt(createdBy, 10),
|
||||||
|
"qr_code": paymentResponse.QrCodeUrl,
|
||||||
|
})
|
||||||
|
|
||||||
|
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: "MIDTRANS",
|
||||||
PaymentType: order.PaymentType,
|
PaymentType: order.PaymentType,
|
||||||
Amount: order.Amount,
|
Amount: order.Amount,
|
||||||
State: "PENDING",
|
State: "PENDING",
|
||||||
@ -362,16 +416,29 @@ func (s OrderService) SumAmount(ctx mycontext.Context, req entity.OrderSearch) (
|
|||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OrderService) GetByID(ctx mycontext.Context, id int64) (*entity.Order, error) {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
order, err := s.repo.FindByID(ctx, id)
|
order, err := s.repo.FindByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ContextLogger(ctx).Error("error when getting products by IDs", zap.Error(err))
|
logger.ContextLogger(ctx).Error("error when getting products by IDs", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if order.CreatedBy != ctx.RequestedBy() {
|
if ctx.IsCasheer() {
|
||||||
return nil, errors2.NewError(errors2.ErrorBadRequest.ErrorType(), "order not found")
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if order.CreatedBy != ctx.RequestedBy() {
|
||||||
|
// return nil, errors2.NewError(errors2.ErrorBadRequest.ErrorType(), "order not found")
|
||||||
|
//}
|
||||||
|
|
||||||
return order, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -119,7 +119,7 @@ type Order interface {
|
|||||||
SumAmount(ctx mycontext.Context, req entity.OrderSearch) (*entity.Order, error)
|
SumAmount(ctx mycontext.Context, req entity.OrderSearch) (*entity.Order, error)
|
||||||
GetDailySales(ctx mycontext.Context, req entity.OrderSearch) ([]entity.ProductDailySales, error)
|
GetDailySales(ctx mycontext.Context, req entity.OrderSearch) ([]entity.ProductDailySales, error)
|
||||||
GetPaymentDistribution(ctx mycontext.Context, req entity.OrderSearch) ([]entity.PaymentTypeDistribution, error)
|
GetPaymentDistribution(ctx mycontext.Context, req entity.OrderSearch) ([]entity.PaymentTypeDistribution, error)
|
||||||
GetByID(ctx mycontext.Context, id int64) (*entity.Order, error)
|
GetByID(ctx mycontext.Context, id int64, referenceID string) (*entity.Order, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OSSService interface {
|
type OSSService interface {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user