From 3e6f5f3d9ef249278649d91e28f51798dc9b5d1b Mon Sep 17 00:00:00 2001 From: ErfanTech Date: Tue, 19 Mar 2024 00:29:31 +0330 Subject: [PATCH] feat: add zibal payment gateway --- README.md | 3 +- _example/zibal/zibal.go | 29 +++++++++++++ providers/zibal/models.go | 42 +++++++++++++++++++ providers/zibal/zibal.go | 85 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 _example/zibal/zibal.go create mode 100644 providers/zibal/models.go create mode 100644 providers/zibal/zibal.go diff --git a/README.md b/README.md index 703e199..6aab736 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ go get -u github.com/GoFarsi/paygap ## Usage -example for zarinpal provider ([Other examples](_examples)) +example for zarinpal provider ([Other examples](https://github.com/GoFarsi/paygap/tree/main/_example)) ```go package main @@ -60,3 +60,4 @@ func main() { - [ ] parsian - [ ] pasargad - [x] sadad +- [x] [zibal](https://zibal.ir) diff --git a/_example/zibal/zibal.go b/_example/zibal/zibal.go new file mode 100644 index 0000000..98bcdfb --- /dev/null +++ b/_example/zibal/zibal.go @@ -0,0 +1,29 @@ +package main + +import ( + "context" + "github.com/GoFarsi/paygap/client" + "github.com/GoFarsi/paygap/providers/zibal" + "log" +) + +func main() { + c := client.New() + + z, err := zibal.New(c, "zibal") + if err != nil { + log.Fatal(err) + } + + resp, err := z.RequestPayment(context.Background(), 5000, "https://example.com", "description") + if err != nil { + log.Fatal(err) + } + + trackID := resp.TrackID + + _, err = z.VerifyPayment(context.Background(), trackID) + if err != nil { + log.Fatal(err) + } +} diff --git a/providers/zibal/models.go b/providers/zibal/models.go new file mode 100644 index 0000000..a4879d7 --- /dev/null +++ b/providers/zibal/models.go @@ -0,0 +1,42 @@ +package zibal + +import "github.com/GoFarsi/paygap/client" + +type Zibal struct { + client client.Transporter + merchant string `validate:"required"` + + baseUrl string + requestEndpoint string + verifyEndpoint string +} + +type paymentRequest struct { + Merchant string `json:"merchant" validate:"required"` + Amount uint `json:"amount" validate:"required,min=1000"` + CallbackURL string `json:"callbackUrl" validate:"required,url"` + Description string `json:"description"` +} + +type PaymentResponse struct { + Result int `json:"result"` + TrackID int `json:"trackId"` + Message string `json:"message"` +} + +type VerificationRequest struct { + Merchant string `json:"merchant" validate:"required"` + TrackID int `json:"trackId" validate:"required"` +} + +type VerificationResponse struct { + PaidAt string `json:"paidAt"` + Amount int `json:"amount"` + Result int `json:"result"` + Status int `json:"status"` + RefNumber int `json:"refNumber"` + Description string `json:"description"` + CardNumber string `json:"cardNumber"` + OrderID string `json:"orderId"` + Message string `json:"message"` +} diff --git a/providers/zibal/zibal.go b/providers/zibal/zibal.go new file mode 100644 index 0000000..9a5b48b --- /dev/null +++ b/providers/zibal/zibal.go @@ -0,0 +1,85 @@ +package zibal + +import ( + "context" + "github.com/GoFarsi/paygap/client" + "github.com/GoFarsi/paygap/status" + "net/http" + + "google.golang.org/grpc/codes" +) + +const zibalBaseURL = "https://gateway.zibal.ir" +const ( + zibalRequestEndpoint = "/v1/request" + zibalVerifyEndpoint = "/v1/verify" +) + +// New creates a zibal provider object for user factory request methods +func New(client client.Transporter, merchant string) (*Zibal, error) { + if client == nil { + return nil, status.ERR_CLIENT_IS_NIL + } + + zibal := &Zibal{ + client: client, + merchant: merchant, + baseUrl: zibalBaseURL, + requestEndpoint: zibalRequestEndpoint, + verifyEndpoint: zibalVerifyEndpoint, + } + if err := client.GetValidator().Struct(zibal); err != nil { + return nil, status.New(0, http.StatusBadRequest, codes.InvalidArgument, err.Error()) + } + + return zibal, nil +} + +// RequestPayment creates a payment request and returns a status code and authority. +func (z *Zibal) RequestPayment(ctx context.Context, amount uint, callBackUrl, description string) (*PaymentResponse, error) { + req := &paymentRequest{ + Merchant: z.merchant, + Amount: amount, + CallbackURL: callBackUrl, + Description: description, + } + if err := z.client.GetValidator().Struct(req); err != nil { + return nil, status.New(0, http.StatusBadRequest, codes.InvalidArgument, err.Error()) + } + + response := &PaymentResponse{} + resp, err := z.client.Post(ctx, &client.APIConfig{Host: z.baseUrl, Path: z.requestEndpoint, Headers: map[string]string{"Content-Type": "application/json"}}, req) + if err != nil { + return nil, status.New(0, http.StatusInternalServerError, codes.Internal, err.Error()) + } + + if err := resp.GetJSON(response); err != nil { + return nil, status.New(0, http.StatusInternalServerError, codes.Internal, err.Error()) + } + + return response, nil +} + +// VerifyPayment verifies a payment and returns the payment details. +func (z *Zibal) VerifyPayment(ctx context.Context, trackID int) (*VerificationResponse, error) { + req := &VerificationRequest{ + Merchant: z.merchant, + TrackID: trackID, + } + + if err := z.client.GetValidator().Struct(req); err != nil { + return nil, status.New(0, http.StatusBadRequest, codes.InvalidArgument, err.Error()) + } + + response := &VerificationResponse{} + resp, err := z.client.Post(ctx, &client.APIConfig{Host: z.baseUrl, Path: z.verifyEndpoint, Headers: map[string]string{"Content-Type": "application/json"}}, req) + if err != nil { + return nil, status.New(0, http.StatusInternalServerError, codes.Internal, err.Error()) + } + + if err := resp.GetJSON(response); err != nil { + return nil, status.New(0, http.StatusInternalServerError, codes.Internal, err.Error()) + } + + return response, nil +}