Implemented gateway interactions
This commit is contained in:
parent
80602afe58
commit
5dbf767863
|
@ -1,3 +1,4 @@
|
||||||
.idea/**
|
.idea/**
|
||||||
template
|
payment-poc
|
||||||
.env
|
.env
|
||||||
|
.env.docker
|
|
@ -14,9 +14,9 @@ below, or create .env file and copy variables below
|
||||||
```
|
```
|
||||||
PSQL_HOST=localhost
|
PSQL_HOST=localhost
|
||||||
PSQL_PORT=5432
|
PSQL_PORT=5432
|
||||||
PSQL_USER=template
|
PSQL_USER=payment-poc
|
||||||
PSQL_PASSWORD=templatePassword
|
PSQL_PASSWORD=paymentPassword
|
||||||
PSQL_DB=template
|
PSQL_DB=payment-poc
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, database is required for template to start, so you can start it with `docker compose up -d`
|
Also, database is required for template to start, so you can start it with `docker compose up -d`
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "wspay"
|
||||||
|
(
|
||||||
|
"id" uuid NOT NULL,
|
||||||
|
"shop_id" varchar(128) NOT NULL,
|
||||||
|
"shopping_card_id" varchar(128) NOT NULL,
|
||||||
|
"total_amount" int NOT NULL,
|
||||||
|
|
||||||
|
"lang" varchar(128) DEFAULT '',
|
||||||
|
|
||||||
|
"customer_first_name" varchar(128) DEFAULT '',
|
||||||
|
"customer_last_name" varchar(128) DEFAULT '',
|
||||||
|
"customer_address" varchar(128) DEFAULT '',
|
||||||
|
"customer_city" varchar(128) DEFAULT '',
|
||||||
|
"customer_zip" varchar(128) DEFAULT '',
|
||||||
|
"customer_country" varchar(128) DEFAULT '',
|
||||||
|
"customer_phone" varchar(128) DEFAULT '',
|
||||||
|
|
||||||
|
"payment_plan" varchar(128) DEFAULT '',
|
||||||
|
"credit_card_name" varchar(128) DEFAULT '',
|
||||||
|
"credit_card_number" varchar(128) DEFAULT '',
|
||||||
|
"payment_method" varchar(128) DEFAULT '',
|
||||||
|
"currency_code" int DEFAULT 0,
|
||||||
|
|
||||||
|
"date_time" timestamp DEFAULT current_timestamp,
|
||||||
|
|
||||||
|
"eci" varchar(256) DEFAULT '',
|
||||||
|
"stan" varchar(256) DEFAULT '',
|
||||||
|
|
||||||
|
"success" int DEFAULT 0,
|
||||||
|
"approval_code" varchar(256) DEFAULT '',
|
||||||
|
"error_message" varchar(256) DEFAULT '',
|
||||||
|
"error_codes" varchar(256) DEFAULT '',
|
||||||
|
|
||||||
|
"payment_state" varchar(256) DEFAULT '',
|
||||||
|
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
CONSTRAINT unique_id UNIQUE ("shopping_card_id")
|
||||||
|
);
|
|
@ -2,14 +2,14 @@ version: '3.1'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
image: registry.bbr-dev.info/template/backend:latest
|
image: registry.bbr-dev.info/payment-poc/backend:latest
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
- database
|
- database
|
||||||
ports:
|
ports:
|
||||||
- "5281:5281"
|
- "5281:5281"
|
||||||
networks:
|
networks:
|
||||||
- template
|
- payment-poc
|
||||||
env_file:
|
env_file:
|
||||||
- .env.docker
|
- .env.docker
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=template
|
- POSTGRES_USER=payment-poc
|
||||||
- POSTGRES_PASSWORD=templatePassword
|
- POSTGRES_PASSWORD=paymentPassword
|
||||||
- POSTGRES_DB=template
|
- POSTGRES_DB=payment-poc
|
||||||
networks:
|
networks:
|
||||||
- template
|
- payment-poc
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
template:
|
payment-poc:
|
|
@ -6,6 +6,6 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=template
|
- POSTGRES_USER=payment-poc
|
||||||
- POSTGRES_PASSWORD=templatePassword
|
- POSTGRES_PASSWORD=paymentPassword
|
||||||
- POSTGRES_DB=template
|
- POSTGRES_DB=payment-poc
|
|
@ -10,10 +10,12 @@ COPY go.mod go.sum ./
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
RUN go build -tags timetzdata template
|
RUN go build -tags timetzdata payment-poc
|
||||||
|
|
||||||
### Stage 2: Run ###
|
### Stage 2: Run ###
|
||||||
FROM scratch
|
FROM scratch
|
||||||
|
WORKDIR /
|
||||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||||
COPY --from=go-build /root/template /usr/bin/template
|
COPY --from=go-build /root/payment-poc /usr/bin/payment-poc
|
||||||
ENTRYPOINT ["template"]
|
ADD templates /templates
|
||||||
|
ENTRYPOINT ["payment-poc"]
|
||||||
|
|
226
main.go
226
main.go
|
@ -2,19 +2,34 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"template/migration"
|
"payment-poc/migration"
|
||||||
|
"payment-poc/wspay"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed db/dev/*.sql
|
//go:embed db/dev/*.sql
|
||||||
var devMigrations embed.FS
|
var devMigrations embed.FS
|
||||||
|
|
||||||
|
var BackendUrl string
|
||||||
|
var ShopId string
|
||||||
|
var ShopSecret string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
godotenv.Load()
|
godotenv.Load()
|
||||||
|
|
||||||
|
BackendUrl = envMustExist("BACKEND_URL")
|
||||||
|
ShopId = envMustExist("SHOP_ID")
|
||||||
|
ShopSecret = envMustExist("SHOP_SECRET")
|
||||||
|
|
||||||
log.SetPrefix("")
|
log.SetPrefix("")
|
||||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +44,36 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
g := gin.Default()
|
g := gin.Default()
|
||||||
|
g.Use(gin.BasicAuth(getAccounts()))
|
||||||
|
|
||||||
|
g.SetFuncMap(template.FuncMap{
|
||||||
|
"formatCurrency": func(current int64) string {
|
||||||
|
return fmt.Sprintf("%d,%02d", current/100, current%100)
|
||||||
|
},
|
||||||
|
"formatState": func(state wspay.PaymentState) string {
|
||||||
|
switch state {
|
||||||
|
case wspay.StateCanceled:
|
||||||
|
return "Otkazano"
|
||||||
|
case wspay.StateAccepted:
|
||||||
|
return "Prihvačeno"
|
||||||
|
case wspay.StateError:
|
||||||
|
return "Greška"
|
||||||
|
case wspay.StateInitialized:
|
||||||
|
return "Inicijalna izrada"
|
||||||
|
case wspay.StateCanceledInitialization:
|
||||||
|
return "Otkazano tijekom izrade"
|
||||||
|
case wspay.StateCompleted:
|
||||||
|
return "Završeno"
|
||||||
|
}
|
||||||
|
return "nepoznato stanje '" + string(state) + "'"
|
||||||
|
},
|
||||||
|
"omitempty": func(value string) string {
|
||||||
|
if value == "" {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
g.NoRoute(func(c *gin.Context) {
|
g.NoRoute(func(c *gin.Context) {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"message": "no action on given url", "created": time.Now()})
|
c.JSON(http.StatusNotFound, gin.H{"message": "no action on given url", "created": time.Now()})
|
||||||
|
@ -36,9 +81,186 @@ func main() {
|
||||||
g.NoMethod(func(c *gin.Context) {
|
g.NoMethod(func(c *gin.Context) {
|
||||||
c.JSON(http.StatusMethodNotAllowed, gin.H{"message": "no action on given method", "created": time.Now()})
|
c.JSON(http.StatusMethodNotAllowed, gin.H{"message": "no action on given method", "created": time.Now()})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
wspayService := wspay.Service{
|
||||||
|
DB: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
g.LoadHTMLGlob("./templates/*.gohtml")
|
||||||
|
|
||||||
g.GET("/", func(c *gin.Context) {
|
g.GET("/", func(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "hello world", "created": time.Now()})
|
entries, err := wspayService.FetchAll()
|
||||||
|
log.Printf("%v", err)
|
||||||
|
c.HTML(200, "index.gohtml", gin.H{"Entries": entries})
|
||||||
|
})
|
||||||
|
g.GET("/initial", func(c *gin.Context) {
|
||||||
|
amount, err := strconv.ParseFloat(c.Query("amount"), 64)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err := wspayService.CreateEntry(ShopId, int64(amount*100))
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
form := wspay.WsPayForm{
|
||||||
|
ShopID: ShopId,
|
||||||
|
ShoppingCartID: entry.ShoppingCartID,
|
||||||
|
Version: "2.0",
|
||||||
|
TotalAmount: entry.TotalAmount,
|
||||||
|
ReturnURL: BackendUrl + "/initial/success",
|
||||||
|
ReturnErrorURL: BackendUrl + "/initial/error",
|
||||||
|
CancelURL: BackendUrl + "/initial/cancel",
|
||||||
|
Signature: wspay.CalculateFormSignature(ShopId, ShopSecret, entry.ShoppingCartID, entry.TotalAmount),
|
||||||
|
}
|
||||||
|
|
||||||
|
c.HTML(200, "initial.gohtml", gin.H{"Action": wspay.AuthorisationForm, "Form": form})
|
||||||
|
})
|
||||||
|
g.GET("/initial/success", func(c *gin.Context) {
|
||||||
|
response := wspay.WsPayFormReturn{}
|
||||||
|
if err := c.ShouldBind(&response); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entry, err := wspayService.FetchByShoppingCartID(response.ShoppingCartID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := wspay.CompareFormReturnSignature(response.Signature, ShopId, ShopSecret, response.ShoppingCartID, response.Success, response.ApprovalCode); err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Lang = response.Lang
|
||||||
|
entry.CustomerFirstName = response.CustomerFirstName
|
||||||
|
entry.CustomerLastName = response.CustomerSurname
|
||||||
|
entry.CustomerAddress = response.CustomerAddress
|
||||||
|
entry.CustomerCity = response.CustomerCity
|
||||||
|
entry.CustomerZIP = response.CustomerZIP
|
||||||
|
entry.CustomerCountry = response.CustomerCountry
|
||||||
|
entry.CustomerPhone = response.CustomerPhone
|
||||||
|
|
||||||
|
entry.PaymentPlan = response.PaymentPlan
|
||||||
|
entry.CreditCardNumber = response.CreditCardNumber
|
||||||
|
entry.DateTime = parseDateTime(response.DateTime)
|
||||||
|
|
||||||
|
entry.ECI = response.ECI
|
||||||
|
entry.STAN = response.STAN
|
||||||
|
|
||||||
|
entry.Success = response.Success
|
||||||
|
entry.ApprovalCode = response.ApprovalCode
|
||||||
|
entry.ErrorMessage = response.ErrorMessage
|
||||||
|
|
||||||
|
entry.State = wspay.StateAccepted
|
||||||
|
|
||||||
|
if err := wspayService.Update(entry); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Query("iframe") != "" {
|
||||||
|
c.HTML(200, "iframe_handler.gohtml", gin.H{})
|
||||||
|
} else {
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
g.GET("/iframe", func(c *gin.Context) {
|
||||||
|
c.HTML(200, "iframe_handler.gohtml", gin.H{})
|
||||||
|
})
|
||||||
|
g.GET("/initial/error", func(c *gin.Context) {
|
||||||
|
response := wspay.WsPayFormError{}
|
||||||
|
if err := c.ShouldBind(&response); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entry, err := wspayService.FetchByShoppingCartID(response.ShoppingCartID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Lang = response.Lang
|
||||||
|
entry.CustomerFirstName = response.CustomerFirstName
|
||||||
|
entry.CustomerLastName = response.CustomerSurname
|
||||||
|
entry.CustomerAddress = response.CustomerAddress
|
||||||
|
entry.CustomerCity = response.CustomerCity
|
||||||
|
entry.CustomerZIP = response.CustomerZIP
|
||||||
|
entry.CustomerCountry = response.CustomerCountry
|
||||||
|
entry.CustomerPhone = response.CustomerPhone
|
||||||
|
|
||||||
|
entry.PaymentPlan = response.PaymentPlan
|
||||||
|
entry.DateTime = parseDateTime(response.DateTime)
|
||||||
|
|
||||||
|
entry.ECI = response.ECI
|
||||||
|
|
||||||
|
entry.Success = response.Success
|
||||||
|
entry.ApprovalCode = response.ApprovalCode
|
||||||
|
entry.ErrorMessage = response.ErrorMessage
|
||||||
|
entry.ErrorCodes = response.ErrorCodes
|
||||||
|
|
||||||
|
entry.State = wspay.StateError
|
||||||
|
|
||||||
|
if err := wspayService.Update(entry); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Query("iframe") != "" {
|
||||||
|
c.HTML(200, "iframe_handler.gohtml", gin.H{})
|
||||||
|
} else {
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
g.GET("info/:id", func(c *gin.Context) {
|
||||||
|
id := uuid.MustParse(c.Param("id"))
|
||||||
|
entry, err := wspayService.FetchById(id)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusNotFound, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(200, "info.gohtml", gin.H{"Entry": entry})
|
||||||
|
})
|
||||||
|
g.GET("/initial/cancel", func(c *gin.Context) {
|
||||||
|
response := wspay.WsPayFormCancel{}
|
||||||
|
if err := c.ShouldBind(&response); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entry, err := wspayService.FetchByShoppingCartID(response.ShoppingCartID)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entry.State = wspay.StateCanceledInitialization
|
||||||
|
|
||||||
|
if err := wspayService.Update(entry); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Query("iframe") != "" {
|
||||||
|
c.HTML(200, "iframe_handler.gohtml", gin.H{})
|
||||||
|
} else {
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Fatal(http.ListenAndServe(":5281", g))
|
log.Fatal(http.ListenAndServe(":5281", g))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAccounts() gin.Accounts {
|
||||||
|
auth := strings.Split(envMustExist("AUTH"), ":")
|
||||||
|
return gin.Accounts{auth[0]: auth[1]}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDateTime(dateTime string) time.Time {
|
||||||
|
t, err := time.Parse("20060102150405", dateTime)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("couldn't parse response time %s: %v", dateTime, err)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
18
makefile
18
makefile
|
@ -10,17 +10,17 @@ setup:
|
||||||
go get
|
go get
|
||||||
|
|
||||||
docker-dev:
|
docker-dev:
|
||||||
docker image build -t registry.bbr-dev.info/template/backend:$(VERSION)-dev .
|
docker image build -t registry.bbr-dev.info/payment-poc/backend:$(VERSION)-dev .
|
||||||
docker tag registry.bbr-dev.info/template/backend:$(VERSION)-dev registry.bbr-dev.info/template/backend:latest-dev
|
docker tag registry.bbr-dev.info/payment-poc/backend:$(VERSION)-dev registry.bbr-dev.info/payment-poc/backend:latest-dev
|
||||||
docker image push registry.bbr-dev.info/template/backend:$(VERSION)-dev
|
docker image push registry.bbr-dev.info/payment-poc/backend:$(VERSION)-dev
|
||||||
docker image push registry.bbr-dev.info/template/backend:latest-dev
|
docker image push registry.bbr-dev.info/payment-poc/backend:latest-dev
|
||||||
|
|
||||||
|
|
||||||
docker-prod:
|
docker-prod:
|
||||||
docker image build -t registry.bbr-dev.info/template/backend:$(VERSION) .
|
docker image build -t registry.bbr-dev.info/payment-poc/backend:$(VERSION) .
|
||||||
docker tag registry.bbr-dev.info/template/backend:$(VERSION) registry.bbr-dev.info/template/backend:latest
|
docker tag registry.bbr-dev.info/payment-poc/backend:$(VERSION) registry.bbr-dev.info/payment-poc/backend:latest
|
||||||
docker image push registry.bbr-dev.info/template/backend:$(VERSION)
|
docker image push registry.bbr-dev.info/payment-poc/backend:$(VERSION)
|
||||||
docker image push registry.bbr-dev.info/template/backend:latest
|
docker image push registry.bbr-dev.info/payment-poc/backend:latest
|
||||||
|
|
||||||
release:
|
release:
|
||||||
git tag $(VERSION)
|
git tag $(VERSION)
|
||||||
|
@ -30,4 +30,4 @@ test:
|
||||||
go test ./...
|
go test ./...
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf template
|
rm -rf payment-poc
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Obrada odgovora</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
|
||||||
|
<style>
|
||||||
|
th {text-align: left}
|
||||||
|
h2 {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="container">
|
||||||
|
|
||||||
|
<p>Obrada odgovora...</p>
|
||||||
|
<div id="error"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window?.top?.postMessage(JSON.stringify({"success": true}), {targetOrigin: "*"});
|
||||||
|
if(window?.top) {
|
||||||
|
document.querySelector("#error").innerHTML = `<p>Izgleda da je došlo do greške, jer stranica nije otvorena u iframe-u</p><a href="/">Stisnite ovdje da se vratite na naslovnicu</a>`;
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Index</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
|
||||||
|
<style>
|
||||||
|
th {text-align: left}
|
||||||
|
tr > td:nth-child(2) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
td, th {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="container">
|
||||||
|
<h2>Novo plačanje</h2>
|
||||||
|
|
||||||
|
<form method="get" action="/initial">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="amount">Vrijednost</label>
|
||||||
|
<input class="form-control" id="amount" required name="amount" type="number" step="0.01" min="0">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="submit">Izradi novo plačanje</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2>Plačanja</h2>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Id</th>
|
||||||
|
<th>Vrijednost</th>
|
||||||
|
<th>Stanje</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{range .Entries}}
|
||||||
|
<tr>
|
||||||
|
<td><a class="link-primary" href="/info/{{.Id}}">{{.Id}}</a></td>
|
||||||
|
<td>{{formatCurrency .TotalAmount}}</td>
|
||||||
|
<td>{{formatState .State}}</td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Info</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
|
||||||
|
<style>
|
||||||
|
th {text-align: left}
|
||||||
|
h2 {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="container">
|
||||||
|
<h2>Plačanje {{.Entry.Id}}</h2>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<tr><th>CartId: </th><td>{{.Entry.ShoppingCartID}}</td></tr>
|
||||||
|
<tr><th>Ukupna vrijednost: </th><td>{{formatCurrency .Entry.TotalAmount}}</td></tr>
|
||||||
|
<tr><th>Jezik: </th><td>{{omitempty .Entry.Lang}}</td></tr>
|
||||||
|
|
||||||
|
<tr><th>Ime: </th><td>{{omitempty .Entry.CustomerFirstName}}</td></tr>
|
||||||
|
<tr><th>Prezime: </th><td>{{omitempty .Entry.CustomerLastName}}</td></tr>
|
||||||
|
<tr><th>Adresa: </th><td>{{omitempty .Entry.CustomerAddress}}</td></tr>
|
||||||
|
<tr><th>Grad: </th><td>{{omitempty .Entry.CustomerCity}}</td></tr>
|
||||||
|
<tr><th>ZIP: </th><td>{{omitempty .Entry.CustomerZIP}}</td></tr>
|
||||||
|
<tr><th>Zemlja: </th><td>{{omitempty .Entry.CustomerCountry}}</td></tr>
|
||||||
|
<tr><th>Broj telefona: </th><td>{{omitempty .Entry.CustomerPhone}}</td></tr>
|
||||||
|
|
||||||
|
<tr><th>Plan plačanja: </th><td>{{omitempty .Entry.PaymentPlan}}</td></tr>
|
||||||
|
<tr><th>Ime kartice: </th><td>{{omitempty .Entry.CreditCardName}}</td></tr>
|
||||||
|
<tr><th>Broj kartice: </th><td>{{omitempty .Entry.CreditCardNumber}}</td></tr>
|
||||||
|
<tr><th>Metoda plačanja: </th><td>{{omitempty .Entry.PaymentMethod}}</td></tr>
|
||||||
|
<tr><th>Oznaka valute: </th><td>{{.Entry.CurrencyCode}}</td></tr>
|
||||||
|
|
||||||
|
<tr><th>Datum i vrijeme: </th><td>{{.Entry.DateTime.Format "Jan 02, 2006 15:04:05 UTC"}}</td></tr>
|
||||||
|
<tr><th>Uspjeh: </th> <td>{{.Entry.Success}}</td></tr>
|
||||||
|
<tr><th>Kod: </th> <td>{{omitempty .Entry.ApprovalCode}}</td></tr>
|
||||||
|
<tr><th>Poruka greške: </th> <td>{{omitempty .Entry.ErrorMessage}}</td></tr>
|
||||||
|
<tr><th>Kodovi greške: </th> <td>{{omitempty .Entry.ErrorCodes}}</td></tr>
|
||||||
|
|
||||||
|
<tr><th>Stanje: </th><td>{{formatState .Entry.State}}</td></tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Izradi plančanje</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
|
||||||
|
<style>
|
||||||
|
h2 {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="container" style="margin-top: 32px">
|
||||||
|
<h2>Započni proces plačanja</h2>
|
||||||
|
|
||||||
|
<form action="{{.Action}}" method="POST">
|
||||||
|
<input type="hidden" name="ShopID" value="{{.Form.ShopID}}">
|
||||||
|
<input type="hidden" name="ShoppingCartID" value="{{.Form.ShoppingCartID}}">
|
||||||
|
<input type="hidden" name="Version" value="{{.Form.Version}}">
|
||||||
|
<input type="hidden" name="TotalAmount" value="{{formatCurrency .Form.TotalAmount}}">
|
||||||
|
<input type="hidden" name="Signature" value="{{.Form.Signature}}">
|
||||||
|
<input type="hidden" name="ReturnURL" value="{{.Form.ReturnURL}}">
|
||||||
|
<input type="hidden" name="CancelURL" value="{{.Form.CancelURL}}">
|
||||||
|
<input type="hidden" name="ReturnErrorURL" value="{{.Form.ReturnErrorURL}}">
|
||||||
|
<input type="submit" class="btn btn-primary" value="Koristi normalni redirect">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Započni normalni proces u iframe-u</h2>
|
||||||
|
|
||||||
|
<form target="payment-frame" name="pay" action="{{.Action}}" method="POST">
|
||||||
|
<input type="hidden" name="Iframe" value="True">
|
||||||
|
<input type="hidden" name="IframeResponseTarget" value="SELF">
|
||||||
|
|
||||||
|
<input type="hidden" name="ShopID" value="{{.Form.ShopID}}">
|
||||||
|
<input type="hidden" name="ShoppingCartID" value="{{.Form.ShoppingCartID}}">
|
||||||
|
<input type="hidden" name="Version" value="{{.Form.Version}}">
|
||||||
|
<input type="hidden" name="TotalAmount" value="{{formatCurrency .Form.TotalAmount}}">
|
||||||
|
<input type="hidden" name="Signature" value="{{.Form.Signature}}">
|
||||||
|
<input type="hidden" name="ReturnURL" value="{{.Form.ReturnURL}}?iframe=true">
|
||||||
|
<input type="hidden" name="CancelURL" value="{{.Form.CancelURL}}?iframe=true">
|
||||||
|
<input type="hidden" name="ReturnErrorURL" value="{{.Form.ReturnErrorURL}}?iframe=true">
|
||||||
|
<input type="submit" class="btn btn-primary" value="Koristi navigaciju u iframe-u">
|
||||||
|
</form>
|
||||||
|
<iframe id="payment-frame" name="payment-frame" style="width: 100%; min-height: 600px"></iframe>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.addEventListener(
|
||||||
|
"message",
|
||||||
|
(event) => {
|
||||||
|
console.log("received response")
|
||||||
|
window.location.href = "/";
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,57 @@
|
||||||
|
package wspay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PaymentState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// initial state
|
||||||
|
StateInitialized PaymentState = "initialized"
|
||||||
|
|
||||||
|
// state on response
|
||||||
|
StateAccepted PaymentState = "accepted"
|
||||||
|
StateError PaymentState = "error"
|
||||||
|
StateCanceledInitialization PaymentState = "canceled_initialization"
|
||||||
|
|
||||||
|
// state after confirmation
|
||||||
|
StateCompleted PaymentState = "completed"
|
||||||
|
StateVoided PaymentState = "voided"
|
||||||
|
StateCanceled PaymentState = "canceled"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WsPayDb struct {
|
||||||
|
Id uuid.UUID `db:"id"`
|
||||||
|
ShopID string `db:"shop_id"`
|
||||||
|
ShoppingCartID string `db:"shopping_card_id"`
|
||||||
|
TotalAmount int64 `db:"total_amount"`
|
||||||
|
Lang string `db:"lang"`
|
||||||
|
|
||||||
|
CustomerFirstName string `db:"customer_first_name"`
|
||||||
|
CustomerLastName string `db:"customer_last_name"`
|
||||||
|
CustomerAddress string `db:"customer_address"`
|
||||||
|
CustomerCity string `db:"customer_city"`
|
||||||
|
CustomerZIP string `db:"customer_zip"`
|
||||||
|
CustomerCountry string `db:"customer_country"`
|
||||||
|
CustomerPhone string `db:"customer_phone"`
|
||||||
|
|
||||||
|
PaymentPlan string `db:"payment_plan"`
|
||||||
|
CreditCardName string `db:"credit_card_name"`
|
||||||
|
CreditCardNumber string `db:"credit_card_number"`
|
||||||
|
PaymentMethod string `db:"payment_method"`
|
||||||
|
CurrencyCode int `db:"currency_code"`
|
||||||
|
|
||||||
|
DateTime time.Time `db:"date_time"`
|
||||||
|
|
||||||
|
ECI string `db:"eci"`
|
||||||
|
STAN string `db:"stan"`
|
||||||
|
|
||||||
|
Success int `db:"success"`
|
||||||
|
ApprovalCode string `db:"approval_code"`
|
||||||
|
ErrorMessage string `db:"error_message"`
|
||||||
|
ErrorCodes string `db:"error_codes"`
|
||||||
|
|
||||||
|
State PaymentState `db:"payment_state"`
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package wspay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
DB *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
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: 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,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return WsPayDb{}, err
|
||||||
|
}
|
||||||
|
return s.FetchById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) FetchAll() ([]WsPayDb, error) {
|
||||||
|
var entries []WsPayDb
|
||||||
|
err := s.DB.Select(&entries, `SELECT * FROM "wspay"`)
|
||||||
|
return entries, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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) 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) 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 CalculateFormSignature(shopId string, secret string, cartId string, amount int64) string {
|
||||||
|
/**
|
||||||
|
Represents a signature created from string formatted from following values in a following order using
|
||||||
|
SHA512 algorithm:
|
||||||
|
ShopID
|
||||||
|
SecretKey
|
||||||
|
ShoppingCartID
|
||||||
|
SecretKey
|
||||||
|
TotalAmount
|
||||||
|
SecretKey
|
||||||
|
*/
|
||||||
|
signature := shopId + secret + cartId + secret + strconv.FormatInt(amount, 10) + secret
|
||||||
|
hash := sha512.New()
|
||||||
|
hash.Write([]byte(signature))
|
||||||
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareFormReturnSignature(signature string, shopId string, secret string, cartId string, success int, approvalCode string) error {
|
||||||
|
/**
|
||||||
|
Represents a signature created from string formatted from following values in a following order using
|
||||||
|
SHA512 algorithm:
|
||||||
|
ShopID
|
||||||
|
SecretKey
|
||||||
|
ShoppingCartID
|
||||||
|
SecretKey
|
||||||
|
Success
|
||||||
|
SecretKey
|
||||||
|
ApprovalCode
|
||||||
|
SecretKey
|
||||||
|
Merchant should validate this signature to make sure that the request is originating from WSPayForm.
|
||||||
|
*/
|
||||||
|
calculatedSignature := shopId + secret + cartId + secret + strconv.FormatInt(int64(success), 10) + secret + approvalCode + secret
|
||||||
|
hash := sha512.New()
|
||||||
|
hash.Write([]byte(calculatedSignature))
|
||||||
|
if hex.EncodeToString(hash.Sum(nil)) == signature {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return errors.New("signature mismatch")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
package wspay
|
||||||
|
|
||||||
|
const AuthorisationForm = "https://formtest.wspay.biz/authorization.aspx"
|
||||||
|
|
||||||
|
type WsPayForm struct {
|
||||||
|
// required args
|
||||||
|
ShopID string
|
||||||
|
ShoppingCartID string
|
||||||
|
Version string
|
||||||
|
TotalAmount int64
|
||||||
|
ReturnURL string
|
||||||
|
ReturnErrorURL string
|
||||||
|
CancelURL string
|
||||||
|
Signature string
|
||||||
|
|
||||||
|
// optional args
|
||||||
|
Lang string
|
||||||
|
CustomerFirstName string
|
||||||
|
CustomerLastName string
|
||||||
|
CustomerAddress string
|
||||||
|
CustomerCity string
|
||||||
|
CustomerZIP string
|
||||||
|
CustomerCountry string
|
||||||
|
CustomerPhone string
|
||||||
|
PaymentPlan string
|
||||||
|
CreditCardName string
|
||||||
|
PaymentMethod string
|
||||||
|
IntAmount int64
|
||||||
|
IntCurrency string
|
||||||
|
ReturnMethod string
|
||||||
|
CurrencyCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayFormReturn struct {
|
||||||
|
CustomerFirstName string `form:"CustomerFirstname"`
|
||||||
|
CustomerSurname string `form:"CustomerSurname"`
|
||||||
|
CustomerAddress string `form:"CustomerAddress"`
|
||||||
|
CustomerCity string `form:"CustomerCity"`
|
||||||
|
CustomerZIP string `form:"CustomerZIP"`
|
||||||
|
CustomerCountry string `form:"CustomerCountry"`
|
||||||
|
CustomerPhone string `form:"CustomerPhone"`
|
||||||
|
CustomerEmail string `form:"CustomerEmail"`
|
||||||
|
ShoppingCartID string `form:"ShoppingCartID"`
|
||||||
|
Lang string `form:"Lang"`
|
||||||
|
DateTime string `form:"DateTime"` //yyyymmddHHMMss
|
||||||
|
Amount string `form:"Amount"` // eg. 123,43
|
||||||
|
ECI string `form:"ECI"`
|
||||||
|
STAN string `form:"STAN"`
|
||||||
|
Partner string `form:"Partner"`
|
||||||
|
WsPayOrderId string `form:"WsPayOrderId"`
|
||||||
|
PaymentType string `form:"PaymentType"`
|
||||||
|
CreditCardNumber string `form:"CreditCardNumber"` // masked number
|
||||||
|
PaymentPlan string `form:"PaymentPlan"`
|
||||||
|
ShopPostedPaymentPlan string `form:"ShopPostedPaymentPlan"`
|
||||||
|
ShopPostedLang string `form:"ShopPostedLang"`
|
||||||
|
ShopPostedCreditCardName string `form:"ShopPostedCreditCardName"`
|
||||||
|
Success int `form:"Success"`
|
||||||
|
ApprovalCode string `form:"ApprovalCode"`
|
||||||
|
ErrorMessage string `form:"ErrorMessage"`
|
||||||
|
ShopPostedPaymentMethod string `form:"ShopPostedPaymentMethod"`
|
||||||
|
Signature string `form:"Signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayFormError struct {
|
||||||
|
CustomerFirstName string
|
||||||
|
CustomerSurname string
|
||||||
|
CustomerAddress string
|
||||||
|
CustomerCity string
|
||||||
|
CustomerZIP string
|
||||||
|
CustomerCountry string
|
||||||
|
CustomerPhone string
|
||||||
|
CustomerEmail string
|
||||||
|
ShoppingCartID string
|
||||||
|
Lang string
|
||||||
|
DateTime string //yyyymmddHHMMss
|
||||||
|
Amount string // eg. 123,43
|
||||||
|
ECI string
|
||||||
|
PaymentType string
|
||||||
|
PaymentPlan string
|
||||||
|
ShopPostedPaymentPlan string
|
||||||
|
ShopPostedLang string
|
||||||
|
ShopPostedCreditCardName string
|
||||||
|
Success int
|
||||||
|
ApprovalCode string
|
||||||
|
ErrorMessage string
|
||||||
|
ShopPostedPaymentMethod string
|
||||||
|
ErrorCodes string
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayFormCancel struct {
|
||||||
|
ResponseCode int
|
||||||
|
ShoppingCartID string
|
||||||
|
ApprovalCode string
|
||||||
|
Success int
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayCompletionRequest struct {
|
||||||
|
Version string
|
||||||
|
WsPayOrderId string
|
||||||
|
ShopId string
|
||||||
|
ApprovalCode string
|
||||||
|
STAN string
|
||||||
|
Amount string
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayCompletionResponse struct {
|
||||||
|
WsPayOrderId string
|
||||||
|
ShopId string
|
||||||
|
ApprovalCode string
|
||||||
|
STAN string
|
||||||
|
ErrorMessage string
|
||||||
|
ActionSuccess string
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayStatusCheckRequest struct {
|
||||||
|
Version string
|
||||||
|
ShopId string
|
||||||
|
ShoppingCartId string
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsPayStatusCheckResponse struct {
|
||||||
|
WsPayOrderId string
|
||||||
|
Signature string
|
||||||
|
STAN string
|
||||||
|
ApprovalCode string
|
||||||
|
ShopID string
|
||||||
|
ShoppingCartID string
|
||||||
|
Amount string
|
||||||
|
CurrencyCode string
|
||||||
|
ActionSuccess string
|
||||||
|
Success string // deprecated
|
||||||
|
Authorized int
|
||||||
|
Completed int
|
||||||
|
Voided int
|
||||||
|
Refunded int
|
||||||
|
PaymentPlan string
|
||||||
|
Partner string
|
||||||
|
OnSite int
|
||||||
|
CreditCardName string
|
||||||
|
CreditCardNumber string
|
||||||
|
ECI string
|
||||||
|
CustomerFirstName string
|
||||||
|
CustomerLastName string
|
||||||
|
CustomerCity string
|
||||||
|
CustomerZIP string
|
||||||
|
CustomerCountry string
|
||||||
|
CustomerPhone string
|
||||||
|
CustomerEmail string
|
||||||
|
TransactionDateTime string //yyyymmddHHMMss
|
||||||
|
IsLessThan30DaysFromTransaction bool
|
||||||
|
CanBeCompleted bool
|
||||||
|
CanBeVoided bool
|
||||||
|
CanBeRefunded bool
|
||||||
|
ExpirationDate string
|
||||||
|
}
|
Loading…
Reference in New Issue