WIP Code cleanup

This commit is contained in:
Borna Rajković committed 2023-07-27 22:46:37 +02:00
1 parent b4b0396b30
commit dcc9754d43
14 files changed
+880 -902

No files matched your search

+190 -33
View File
@@ -1,60 +1,186 @@
package wspay
import (
"bytes"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"io"
"net/http"
"payment-poc/database"
"payment-poc/state"
"strconv"
)
type Service struct {
DB *sqlx.DB
Provider *database.PaymentEntryProvider
ShopId string
ShopSecret string
BackendUrl string
}
func (s *Service) CreateEntry(shopId string, totalAmount int64) (WsPayDb, error) {
id := uuid.Must(uuid.NewRandom())
entry := WsPayDb{
Id: id,
ShopID: shopId,
ShoppingCartID: id.String(),
TotalAmount: totalAmount,
State: state.StateInitialized,
}
_, err := s.DB.Exec(`INSERT INTO "wspay" ("id", "shop_id", "shopping_card_id", "total_amount", "payment_state") VALUES ($1, $2, $3, $4, $5)`,
&entry.Id, &entry.ShopID, &entry.ShoppingCartID, &entry.TotalAmount, &entry.State,
)
func (s *Service) CreatePaymentUrl(amount int64) (string, error) {
entry, err := s.Provider.CreateEntry(database.PaymentEntry{
Gateway: state.GatewayVivaWallet,
State: state.StateInitialized,
TotalAmount: amount,
})
if err != nil {
return WsPayDb{}, err
return "", err
}
return s.FetchById(id)
return "/wspay/initialize/" + entry.Id.String(), nil
}
func (s *Service) FetchAll() ([]WsPayDb, error) {
var entries []WsPayDb
err := s.DB.Select(&entries, `SELECT * FROM "wspay"`)
return entries, err
func (s *Service) CompleteTransaction(entry database.PaymentEntry, amount int64) (database.PaymentEntry, error) {
if entry.State == state.StateAccepted {
var request = CompletionRequest{
Version: "2.0",
WsPayOrderId: entry.Id.String(),
ShopId: s.ShopId,
ApprovalCode: *entry.ApprovalCode,
STAN: *entry.STAN,
Amount: amount,
Signature: CalculateCompletionSignature(s.ShopId, s.ShopSecret, entry.Id.String(), *entry.STAN, *entry.ApprovalCode, amount),
}
httpResponse, err := createRequest(
"POST",
"https://test.wspay.biz/api/services/completion",
map[string]string{"content-type": "application/json"},
toJson(request),
)
if err != nil {
return database.PaymentEntry{}, err
}
var response CompletionResponse
err = readResponse(httpResponse, &response)
if err != nil {
return database.PaymentEntry{}, err
}
if CompareCompletionReturnSignature(response.Signature, s.ShopId, s.ShopSecret, entry.Id.String(), *entry.STAN, response.ActionSuccess, response.ApprovalCode) != nil {
entry.Amount = &amount
entry.State = state.StateCompleted
} else {
return database.PaymentEntry{}, errors.New("invalid signature")
}
return entry, nil
} else {
return database.PaymentEntry{}, errors.New("payment is in invalid state")
}
}
func (s *Service) FetchById(id uuid.UUID) (WsPayDb, error) {
entry := WsPayDb{}
err := s.DB.Get(&entry, `SELECT * FROM "wspay" WHERE "id" = $1`, id)
return entry, err
func (s *Service) CancelTransaction(entry database.PaymentEntry) (database.PaymentEntry, error) {
if entry.State == state.StateAccepted {
var request = CompletionRequest{
Version: "2.0",
WsPayOrderId: entry.Id.String(),
ShopId: s.ShopId,
ApprovalCode: *entry.ApprovalCode,
STAN: *entry.STAN,
Amount: entry.TotalAmount,
Signature: CalculateCompletionSignature(s.ShopId, s.ShopSecret, entry.Id.String(), *entry.STAN, *entry.ApprovalCode, entry.TotalAmount),
}
httpResponse, err := createRequest(
"POST",
"https://test.wspay.biz/api/services/void",
map[string]string{"content-type": "application/json"},
toJson(request),
)
if err != nil {
return database.PaymentEntry{}, err
}
var response CompletionResponse
err = readResponse(httpResponse, &response)
if err != nil {
return database.PaymentEntry{}, err
}
if CompareCompletionReturnSignature(response.Signature, s.ShopId, s.ShopSecret, entry.Id.String(), *entry.STAN, response.ActionSuccess, response.ApprovalCode) != nil {
entry.State = state.StateCanceled
} else {
return database.PaymentEntry{}, errors.New("invalid signature")
}
return entry, nil
} else {
return database.PaymentEntry{}, errors.New("payment is in invalid state")
}
}
func (s *Service) FetchByShoppingCartID(id string) (WsPayDb, error) {
entry := WsPayDb{}
err := s.DB.Get(&entry, `SELECT * FROM "wspay" WHERE "shopping_card_id" = $1`, id)
return entry, err
func (s *Service) InitializePayment(entry database.PaymentEntry) Form {
form := Form{
ShopID: s.ShopId,
ShoppingCartID: entry.Id.String(),
Version: "2.0",
TotalAmount: entry.TotalAmount,
ReturnURL: s.BackendUrl + "/wspay/success",
ReturnErrorURL: s.BackendUrl + "/wspay/error",
CancelURL: s.BackendUrl + "/wspay/cancel",
Signature: CalculateFormSignature(s.ShopId, s.ShopSecret, entry.Id.String(), entry.TotalAmount),
}
return form
}
func (s *Service) Update(entry WsPayDb) error {
_, err := s.DB.Exec(`UPDATE "wspay" set "lang" = $2, "customer_first_name" = $3, "customer_last_name" = $4, "customer_address" = $5, "customer_city" = $6, "customer_zip" = $7, "customer_country" = $8, "customer_phone" = $9, "payment_plan" = $10, "credit_card_name" = $11, "credit_card_number" = $12, "payment_method" = $13, "currency_code" = $14, "date_time" = $15, "eci" = $16, "stan" = $17, "success" = $18, "approval_code" = $19, "error_message" = $20, "error_codes" = $21, "payment_state" = $22 WHERE "id" = $1`,
&entry.Id, &entry.Lang, &entry.CustomerFirstName, &entry.CustomerLastName, &entry.CustomerAddress, &entry.CustomerCity, &entry.CustomerZIP, &entry.CustomerCountry, &entry.CustomerPhone, &entry.PaymentPlan, &entry.CreditCardName, &entry.CreditCardNumber, &entry.PaymentMethod, &entry.CurrencyCode, &entry.DateTime, &entry.ECI, &entry.STAN, &entry.Success, &entry.ApprovalCode, &entry.ErrorMessage, &entry.ErrorCodes, &entry.State,
)
return err
func (s *Service) HandleSuccessResponse(c *gin.Context) (string, error) {
response := FormReturn{}
if err := c.ShouldBind(&response); err != nil {
return "", err
}
entry, err := s.Provider.FetchById(uuid.MustParse(response.ShoppingCartID))
if err != nil {
return "", err
}
if err := CompareFormReturnSignature(response.Signature, s.ShopId, s.ShopSecret, response.ShoppingCartID, response.Success, response.ApprovalCode); err != nil {
return "", err
}
entry.Lang = &response.Lang
entry.ECI = &response.ECI
entry.STAN = &response.STAN
entry.Success = &response.Success
entry.ApprovalCode = &response.ApprovalCode
entry.State = state.StateAccepted
if _, err := s.Provider.UpdateEntry(entry); err != nil {
return "", err
}
return "/entries/" + entry.Id.String(), nil
}
func (s *Service) HandleErrorResponse(c *gin.Context, paymentState state.PaymentState) (string, error) {
response := FormError{}
if err := c.ShouldBind(&response); err != nil {
return "", err
}
entry, err := s.Provider.FetchById(uuid.MustParse(response.ShoppingCartID))
if err != nil {
return "", err
}
if err := CompareFormReturnSignature(response.Signature, s.ShopId, s.ShopSecret, response.ShoppingCartID, response.Success, response.ApprovalCode); err != nil {
return "", err
}
entry.Lang = &response.Lang
entry.ECI = &response.ECI
entry.Success = &response.Success
entry.ApprovalCode = &response.ApprovalCode
entry.Error = &response.ErrorMessage
entry.State = paymentState
if _, err := s.Provider.UpdateEntry(entry); err != nil {
return "", err
}
return "/entries/" + entry.Id.String(), nil
}
func CalculateFormSignature(shopId string, secret string, cartId string, amount int64) string {
@@ -141,3 +267,34 @@ func CompareCompletionReturnSignature(signature string, shopId string, secret st
return errors.New("signature mismatch")
}
}
func readResponse[T any](httpResponse *http.Response, response T) error {
if httpResponse.StatusCode == http.StatusOK {
content, err := io.ReadAll(httpResponse.Body)
if err != nil {
return err
}
return json.Unmarshal(content, response)
} else {
return errors.New("received wrong status, expected 200 received " + strconv.FormatInt(int64(httpResponse.StatusCode), 10))
}
}
func createRequest(method string, url string, headers map[string]string, content []byte) (*http.Response, error) {
request, err := http.NewRequest(method, url, bytes.NewReader(content))
if err != nil {
return nil, err
}
for key, value := range headers {
request.Header.Add(key, value)
}
return http.DefaultClient.Do(request)
}
func toJson[T any](request T) []byte {
response, err := json.Marshal(request)
if err != nil {
panic(err)
}
return response
}
+8 -8
View File
@@ -2,7 +2,7 @@ package wspay
const AuthorisationForm = "https://formtest.wspay.biz/authorization.aspx"
type WsPayForm struct {
type Form struct {
// required args
ShopID string
ShoppingCartID string
@@ -31,7 +31,7 @@ type WsPayForm struct {
CurrencyCode int
}
type WsPayFormReturn struct {
type FormReturn struct {
CustomerFirstName string `form:"CustomerFirstname"`
CustomerSurname string `form:"CustomerSurname"`
CustomerAddress string `form:"CustomerAddress"`
@@ -61,7 +61,7 @@ type WsPayFormReturn struct {
Signature string `form:"Signature"`
}
type WsPayFormError struct {
type FormError struct {
CustomerFirstName string
CustomerSurname string
CustomerAddress string
@@ -88,7 +88,7 @@ type WsPayFormError struct {
Signature string
}
type WsPayFormCancel struct {
type FormCancel struct {
ResponseCode int
ShoppingCartID string
ApprovalCode string
@@ -96,7 +96,7 @@ type WsPayFormCancel struct {
Signature string
}
type WsPayCompletionRequest struct {
type CompletionRequest struct {
Version string
WsPayOrderId string
ShopId string
@@ -106,7 +106,7 @@ type WsPayCompletionRequest struct {
Signature string
}
type WsPayCompletionResponse struct {
type CompletionResponse struct {
WsPayOrderId string
ShopId string
ApprovalCode string
@@ -116,14 +116,14 @@ type WsPayCompletionResponse struct {
Signature string
}
type WsPayStatusCheckRequest struct {
type StatusCheckRequest struct {
Version string
ShopId string
ShoppingCartId string
Signature string
}
type WsPayStatusCheckResponse struct {
type StatusCheckResponse struct {
WsPayOrderId string
Signature string
STAN string