Added completion on wspay
This commit is contained in:
parent
9440fa9778
commit
b4b0396b30
123
main.go
123
main.go
|
@ -1,7 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
@ -10,6 +13,7 @@ import (
|
||||||
"github.com/stripe/stripe-go/v72/checkout/session"
|
"github.com/stripe/stripe-go/v72/checkout/session"
|
||||||
"github.com/stripe/stripe-go/v72/paymentintent"
|
"github.com/stripe/stripe-go/v72/paymentintent"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"payment-poc/migration"
|
"payment-poc/migration"
|
||||||
|
@ -39,6 +43,7 @@ func init() {
|
||||||
godotenv.Load()
|
godotenv.Load()
|
||||||
|
|
||||||
BackendUrl = envMustExist("BACKEND_URL")
|
BackendUrl = envMustExist("BACKEND_URL")
|
||||||
|
|
||||||
WsPayShopId = envMustExist("WSPAY_SHOP_ID")
|
WsPayShopId = envMustExist("WSPAY_SHOP_ID")
|
||||||
WsPayShopSecret = envMustExist("WSPAY_SHOP_SECRET")
|
WsPayShopSecret = envMustExist("WSPAY_SHOP_SECRET")
|
||||||
|
|
||||||
|
@ -493,6 +498,124 @@ func setupWsPayEndpoints(g *gin.RouterGroup, wspayService wspay.Service) {
|
||||||
|
|
||||||
c.HTML(200, "wspay.gohtml", gin.H{"Action": wspay.AuthorisationForm, "Form": form})
|
c.HTML(200, "wspay.gohtml", gin.H{"Action": wspay.AuthorisationForm, "Form": form})
|
||||||
})
|
})
|
||||||
|
g.POST("complete/:id", func(c *gin.Context) {
|
||||||
|
id := uuid.MustParse(c.Param("id"))
|
||||||
|
amount, err := strconv.ParseFloat(c.PostForm("amount"), 64)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entry, err := wspayService.FetchById(id)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusNotFound, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if int64(amount*100) > entry.TotalAmount || int64(amount*100) < 1 {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if entry.State == state.StateAccepted {
|
||||||
|
var request = wspay.WsPayCompletionRequest{
|
||||||
|
Version: "2.0",
|
||||||
|
WsPayOrderId: entry.ShoppingCartID,
|
||||||
|
ShopId: entry.ShopID,
|
||||||
|
ApprovalCode: entry.ApprovalCode,
|
||||||
|
STAN: entry.STAN,
|
||||||
|
Amount: int64(amount * 100),
|
||||||
|
Signature: wspay.CalculateCompletionSignature(WsPayShopId, WsPayShopSecret, entry.ShoppingCartID, entry.STAN, entry.ApprovalCode, int64(amount*100)),
|
||||||
|
}
|
||||||
|
|
||||||
|
content, _ := json.Marshal(&request)
|
||||||
|
response, err := http.Post("https://test.wspay.biz/api/services/completion", "application/json", bytes.NewBuffer(content))
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
transactionResponse := wspay.WsPayCompletionResponse{}
|
||||||
|
content, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(content, &transactionResponse); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Printf("Received transaction response: success=%s, errorMessage=%s, approvalCode=%s",
|
||||||
|
transactionResponse.ActionSuccess, transactionResponse.ErrorMessage, transactionResponse.ApprovalCode,
|
||||||
|
)
|
||||||
|
if wspay.CompareCompletionReturnSignature(transactionResponse.Signature, WsPayShopId, WsPayShopSecret, entry.ShoppingCartID, entry.STAN, transactionResponse.ActionSuccess, transactionResponse.ApprovalCode) != nil {
|
||||||
|
entry.TotalAmount = int64(amount * 100)
|
||||||
|
entry.State = state.StateCompleted
|
||||||
|
wspayService.Update(entry)
|
||||||
|
} else {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("received invalid signature"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("received wrong status, expected 200 received "+strconv.FormatInt(int64(response.StatusCode), 10)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusSeeOther, "/wspay/info/"+id.String())
|
||||||
|
})
|
||||||
|
g.POST("cancel/: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
|
||||||
|
}
|
||||||
|
if entry.State == state.StateAccepted {
|
||||||
|
var request = wspay.WsPayCompletionRequest{
|
||||||
|
Version: "2.0",
|
||||||
|
WsPayOrderId: entry.ShoppingCartID,
|
||||||
|
ShopId: entry.ShopID,
|
||||||
|
ApprovalCode: entry.ApprovalCode,
|
||||||
|
STAN: entry.STAN,
|
||||||
|
Amount: entry.TotalAmount,
|
||||||
|
Signature: wspay.CalculateCompletionSignature(WsPayShopId, WsPayShopSecret, entry.ShoppingCartID, entry.STAN, entry.ApprovalCode, entry.TotalAmount),
|
||||||
|
}
|
||||||
|
|
||||||
|
content, _ := json.Marshal(&request)
|
||||||
|
response, err := http.Post("https://test.wspay.biz/api/services/void", "application/json", bytes.NewBuffer(content))
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
transactionResponse := wspay.WsPayCompletionResponse{}
|
||||||
|
content, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(content, &transactionResponse); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Printf("Received transaction response: success=%s, errorMessage=%s, approvalCode=%s",
|
||||||
|
transactionResponse.ActionSuccess, transactionResponse.ErrorMessage, transactionResponse.ApprovalCode,
|
||||||
|
)
|
||||||
|
if wspay.CompareCompletionReturnSignature(transactionResponse.Signature, WsPayShopId, WsPayShopSecret, entry.ShoppingCartID, entry.STAN, transactionResponse.ActionSuccess, transactionResponse.ApprovalCode) != nil {
|
||||||
|
entry.State = state.StateCanceled
|
||||||
|
wspayService.Update(entry)
|
||||||
|
} else {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("received invalid signature"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("received wrong status, expected 200 received "+strconv.FormatInt(int64(response.StatusCode), 10)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Redirect(http.StatusSeeOther, "/wspay/info/"+id.String())
|
||||||
|
})
|
||||||
g.GET("success", func(c *gin.Context) {
|
g.GET("success", func(c *gin.Context) {
|
||||||
response := wspay.WsPayFormReturn{}
|
response := wspay.WsPayFormReturn{}
|
||||||
if err := c.ShouldBind(&response); err != nil {
|
if err := c.ShouldBind(&response); err != nil {
|
||||||
|
|
|
@ -45,5 +45,18 @@
|
||||||
|
|
||||||
<tr><th>Stanje: </th><td>{{formatState .Entry.State}}</td></tr>
|
<tr><th>Stanje: </th><td>{{formatState .Entry.State}}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{{if eq .Entry.State "accepted"}}
|
||||||
|
<form class="mb-3" method="post" action="/wspay/complete/{{.Entry.Id}}">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="amount">Završi transakciju</label>
|
||||||
|
<input class="form-control" id="amount" required name="amount" type="number" value="{{formatCurrency2 .Entry.TotalAmount}}" step="0.01" min="0.01" max="{{formatCurrency2 .Entry.TotalAmount}}">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="submit">Završi transakciju</button>
|
||||||
|
</form>
|
||||||
|
<form method="post" action="/wspay/cancel/{{.Entry.Id}}">
|
||||||
|
<button class="btn btn-primary" type="submit">Otkaži transakciju</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -74,6 +74,27 @@ func CalculateFormSignature(shopId string, secret string, cartId string, amount
|
||||||
return hex.EncodeToString(hash.Sum(nil))
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CalculateCompletionSignature(shopId string, secret string, cartId string, stan string, approvalCode string, amount int64) string {
|
||||||
|
/**
|
||||||
|
Represents a signature created from string formatted from following values in a following order using
|
||||||
|
SHA512 algorithm:
|
||||||
|
ShopID
|
||||||
|
WsPayOrderId
|
||||||
|
SecretKey
|
||||||
|
STAN
|
||||||
|
SecretKey
|
||||||
|
ApprovalCode
|
||||||
|
SecretKey
|
||||||
|
Amount
|
||||||
|
SecretKey
|
||||||
|
WsPayOrderId
|
||||||
|
*/
|
||||||
|
signature := shopId + cartId + secret + stan + secret + approvalCode + secret + strconv.FormatInt(amount, 10) + secret + cartId
|
||||||
|
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 {
|
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
|
Represents a signature created from string formatted from following values in a following order using
|
||||||
|
@ -97,3 +118,26 @@ func CompareFormReturnSignature(signature string, shopId string, secret string,
|
||||||
return errors.New("signature mismatch")
|
return errors.New("signature mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CompareCompletionReturnSignature(signature string, shopId string, secret string, stan string, actionSuccess string, approvalCode string, cartId string) error {
|
||||||
|
/**
|
||||||
|
Represents a signature created from string formatted from following values in a following order using
|
||||||
|
SHA512 algorithm:
|
||||||
|
ShopID
|
||||||
|
SecretKey
|
||||||
|
STAN
|
||||||
|
ActionSuccess
|
||||||
|
SecretKey
|
||||||
|
ApprovalCode
|
||||||
|
WsPayOrderId
|
||||||
|
Merchant should validate this signature to make sure that the request is originating from WSPayForm.
|
||||||
|
*/
|
||||||
|
calculatedSignature := shopId + secret + stan + actionSuccess + secret + approvalCode + cartId
|
||||||
|
hash := sha512.New()
|
||||||
|
hash.Write([]byte(calculatedSignature))
|
||||||
|
if hex.EncodeToString(hash.Sum(nil)) == signature {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return errors.New("signature mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -102,7 +102,7 @@ type WsPayCompletionRequest struct {
|
||||||
ShopId string
|
ShopId string
|
||||||
ApprovalCode string
|
ApprovalCode string
|
||||||
STAN string
|
STAN string
|
||||||
Amount string
|
Amount int64
|
||||||
Signature string
|
Signature string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue