Skip to content

Commit

Permalink
Added draft of transaction verification
Browse files Browse the repository at this point in the history
  • Loading branch information
HirbodBehnam committed Dec 30, 2023
1 parent b3d7745 commit 6b1c393
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 7 deletions.
4 changes: 2 additions & 2 deletions payment/api/goods.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (api *API) AddGood(c *gin.Context) {
var body createGoodRequest
err := c.BindJSON(&body)
if err != nil {
c.JSON(http.StatusBadRequest, err.Error())
c.JSON(http.StatusBadRequest, "cannot parse body: "+err.Error())
return
}
logger := log.WithField("body", body)
Expand All @@ -49,5 +49,5 @@ func (api *API) AddGood(c *gin.Context) {
}
logger.Info("added good to database")
// Report back to endpoint
c.JSON(http.StatusCreated, creteGoodResponse{good.ID})
c.JSON(http.StatusCreated, createGoodResponse{good.ID})
}
67 changes: 66 additions & 1 deletion payment/api/idpay.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func (api *API) CreateTransaction(c *gin.Context) {
var body createTransactionRequest
err := c.BindJSON(&body)
if err != nil {
c.JSON(http.StatusBadRequest, err.Error())
c.JSON(http.StatusBadRequest, "cannot parse body: "+err.Error())
return
}
logger := log.WithField("body", body)
Expand Down Expand Up @@ -78,3 +78,68 @@ func (api *API) CreateTransaction(c *gin.Context) {
})
return
}

// GetTransaction verifies a transaction if not already and then returns the transaction
// information to the requester
func (api *API) GetTransaction(c *gin.Context) {
// At first parse the body
var body getTransactionRequest
err := c.BindJSON(&body)
if err != nil {
c.JSON(http.StatusBadRequest, "cannot parse body: "+err.Error())
return
}
logger := log.WithField("OrderID", body.OrderID)
// Now get the transaction from database
payment := database.Payment{OrderID: body.OrderID}
err = api.Database.GetPayment(&payment)
if err != nil {
logger.WithError(err).Error("cannot get order from database")
c.JSON(http.StatusInternalServerError, "cannot parse body: "+err.Error())
return
}
// Check if it's verified and verify it
if payment.PaymentStatus == database.PaymentStatusInitiated {
// Send the verification request
result, err := api.PaymentService.VerifyTransaction(c.Request.Context(), idpay.TransactionVerificationRequest{
OrderID: payment.OrderID.String(),
ID: payment.ID.String,
})
if err != nil {
logger.WithError(err).Error("cannot verify transaction")
c.JSON(http.StatusInternalServerError, "cannot verify transaction: "+err.Error())
return
}
// Check the result and update database
if result.PaymentOK {
err = api.Database.MarkPaymentAsOK(&payment, result.TrackID, result.PaymentTrackID)
} else {
err = api.Database.MarkPaymentAsFailed(&payment)
}
if err != nil {
// NIGGA
logger.WithField("result", result).WithError(err).Error("cannot write verified transaction result")
c.JSON(http.StatusInternalServerError, "cannot write verified transaction result: "+err.Error())
return
}
}
// Return the converted struct
result := getTransactionResponse{
OrderID: payment.OrderID,
UserID: payment.UserID,
ToPayAmount: payment.ToPayAmount,
Discount: payment.Discount,
Description: payment.Description,
ID: payment.ID.String,
TrackID: payment.TrackID.String,
PaymentTrackID: payment.PaymentTrackID.String,
PaymentStatus: payment.PaymentStatus,
BoughtGoods: make([]string, len(payment.BoughtGoods)),
CreatedAt: payment.CreatedAt,
VerifiedAt: payment.VerifiedAt.Time,
}
for i := range payment.BoughtGoods {
result.BoughtGoods[i] = payment.BoughtGoods[i].Name
}
c.JSON(http.StatusOK, result)
}
44 changes: 41 additions & 3 deletions payment/api/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package api

import "github.com/google/uuid"
import (
"github.com/google/uuid"
"time"
"wss-payment/internal/database"
)

// createTransactionRequest is the request body for create transaction endpoint
type createTransactionRequest struct {
Expand Down Expand Up @@ -45,8 +49,42 @@ type createGoodRequest struct {
Description string `json:"description"`
}

// createTransactionRequest is the result of creating goods endpoint
type creteGoodResponse struct {
// createGoodResponse is the result of creating goods endpoint
type createGoodResponse struct {
// ID of the created good
ID uint32 `json:"id"`
}

// getTransactionRequest is the request body of the get transaction endpoint
type getTransactionRequest struct {
OrderID uuid.UUID `json:"order_id" binding:"required"`
}

// getTransactionResponse is the response to the get transaction result.
// It is mostly based on database.Payment object
type getTransactionResponse struct {
// The order ID which is sent to pay.ir
OrderID uuid.UUID `json:"order_id"`
// Who has made this payment?
UserID uint64 `json:"user_id"`
// What is the amount that the user should pay?
ToPayAmount uint64 `json:"to_pay_amount"`
// The amount which we got a discount
Discount uint64 `json:"discount"`
// An optional description about this payment
Description string `json:"description,omitempty"`
// The ID which is returned from idpay after we have initiated the transaction
ID string `json:"id,omitempty"`
// The track ID which idpay returns to us after verification
TrackID string `json:"track_id,omitempty"`
// The payment track ID which idpay returns to us after verification
PaymentTrackID string `json:"payment_track_id,omitempty"`
// What is the status of this payment?
PaymentStatus database.PaymentStatus `json:"payment_status"`
// List of the Goos which this user has bought in this payment
BoughtGoods []string `json:"bought_goods"`
// When was this payment created?
CreatedAt time.Time `json:"created_at"`
// When was it verified? (could be null)
VerifiedAt time.Time `json:"verified_at,omitempty"`
}
43 changes: 43 additions & 0 deletions payment/internal/database/payment.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package database

import (
"database/sql"
"github.com/go-faster/errors"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
"time"
)

// GetGoods gets the list of all goods in database
Expand Down Expand Up @@ -51,3 +53,44 @@ func (db PaymentDatabase) MarkAsFailed(orderID uuid.UUID) {
log.WithError(err).WithField("OrderID", orderID).Error("cannot mark order as failed")
}
}

// GetPayment will get a payment from database based on its primary key
func (db PaymentDatabase) GetPayment(payment *Payment) error {
return db.db.Find(payment).Error
}

// MarkPaymentAsOK will mark a payment as successful and then updates its track ID, payment track ID and verified at time.
// This function will also update the values in payment argument to the ones which will be inserted in database.
func (db PaymentDatabase) MarkPaymentAsOK(payment *Payment, trackID string, paymentTrackID string) error {
verifiedAt := sql.NullTime{Valid: true, Time: time.Now()}
err := db.db.Model(&Payment{OrderID: payment.OrderID}).Updates(&Payment{
TrackID: sql.NullString{Valid: true, String: trackID},
PaymentTrackID: sql.NullString{Valid: true, String: paymentTrackID},
PaymentStatus: PaymentStatusSuccess,
VerifiedAt: verifiedAt,
}).Error
if err != nil {
return err
}
payment.TrackID = sql.NullString{Valid: true, String: trackID}
payment.PaymentTrackID = sql.NullString{Valid: true, String: paymentTrackID}
payment.PaymentStatus = PaymentStatusSuccess
payment.VerifiedAt = verifiedAt
return nil
}

// MarkPaymentAsFailed will mark a payment as failed and then updates its verified at time.
// This function will also update the values in payment argument to the ones which will be inserted in database.
func (db PaymentDatabase) MarkPaymentAsFailed(payment *Payment) error {
verifiedAt := sql.NullTime{Valid: true, Time: time.Now()}
err := db.db.Model(&Payment{OrderID: payment.OrderID}).Updates(&Payment{
PaymentStatus: PaymentStatusFailed,
VerifiedAt: verifiedAt,
}).Error
if err != nil {
return err
}
payment.PaymentStatus = PaymentStatusFailed
payment.VerifiedAt = verifiedAt
return nil
}
2 changes: 1 addition & 1 deletion payment/internal/database/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Payment struct {
// When was this payment created?
CreatedAt time.Time `gorm:"not null"`
// When was it verified? (could be null)
VerifiedAt time.Time
VerifiedAt sql.NullTime
}

type Good struct {
Expand Down

0 comments on commit 6b1c393

Please sign in to comment.