WIP Code cleanup
This commit is contained in:
1 parent
b4b0396b30
commit
dcc9754d43
14 files changed
+880
-902
No files matched your search
+190
-33
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user