-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6a1f3f5
commit 060f08d
Showing
7 changed files
with
248 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package idpay | ||
|
||
import ( | ||
"context" | ||
"wss-payment/pkg/payment" | ||
) | ||
|
||
type PaymentAdaptor struct { | ||
idpay IDPay | ||
} | ||
|
||
func NewPaymentAdaptor(apiKey string, sandboxed bool) PaymentAdaptor { | ||
return PaymentAdaptor{NewIDPay(apiKey, sandboxed)} | ||
} | ||
|
||
func (adaptor PaymentAdaptor) CreateTransaction(ctx context.Context, req payment.TransactionCreationRequest) (payment.TransactionCreationResult, error) { | ||
idpayRequest := TransactionCreationRequest{ | ||
OrderID: req.OrderID, | ||
Phone: req.UsersPhone, | ||
Mail: req.UsersMail, | ||
Description: req.Description, | ||
Callback: req.Callback, | ||
Amount: req.Amount, | ||
} | ||
idpayResult, err := adaptor.idpay.CreateTransaction(ctx, idpayRequest) | ||
if err != nil { | ||
return payment.TransactionCreationResult{}, err | ||
} | ||
return payment.TransactionCreationResult{ | ||
ServiceOrderID: idpayResult.ID, | ||
RedirectLink: idpayResult.Link, | ||
}, nil | ||
} | ||
|
||
func (adaptor PaymentAdaptor) VerifyTransaction(ctx context.Context, req payment.TransactionVerificationRequest) (payment.TransactionVerificationResult, error) { | ||
idpayRequest := TransactionVerificationRequest{ | ||
OrderID: req.OrderID, | ||
ID: req.ServiceOrderID, | ||
} | ||
idpayResult, err := adaptor.idpay.VerifyTransaction(ctx, idpayRequest) | ||
if err != nil { | ||
return payment.TransactionVerificationResult{}, err | ||
} | ||
return payment.TransactionVerificationResult{ | ||
TrackID: idpayResult.TrackID, | ||
PaymentOK: idpayResult.PaymentOK, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package zarinpal | ||
|
||
import ( | ||
"context" | ||
log "github.com/sirupsen/logrus" | ||
"strconv" | ||
"wss-payment/pkg/payment" | ||
) | ||
|
||
type PaymentAdaptor struct { | ||
zarinpal Zarinpal | ||
} | ||
|
||
func NewPaymentAdaptor(merchantID string) PaymentAdaptor { | ||
return PaymentAdaptor{NewZarinpal(merchantID)} | ||
} | ||
|
||
func (adaptor PaymentAdaptor) CreateTransaction(ctx context.Context, req payment.TransactionCreationRequest) (payment.TransactionCreationResult, error) { | ||
zarinpalRequest := TransactionCreationRequest{ | ||
MerchantID: adaptor.zarinpal.merchantID, | ||
Currency: "IRR", // rial | ||
Description: req.Description, | ||
Callback: req.Callback, | ||
Metadata: TransactionCreationRequestMetadata{ | ||
Phone: req.UsersPhone, | ||
Email: req.UsersMail, | ||
OrderID: req.OrderID, | ||
}, | ||
Amount: req.Amount, | ||
} | ||
if req.Description == "" { // we cannot have empty description in zarinpal | ||
req.Description = "WSS payment for user " + req.UsersPhone | ||
} | ||
zarinpalResult, err := adaptor.zarinpal.CreateTransaction(ctx, zarinpalRequest) | ||
if err != nil { | ||
return payment.TransactionCreationResult{}, err | ||
} | ||
return payment.TransactionCreationResult{ | ||
ServiceOrderID: zarinpalResult.Data.Authority, | ||
RedirectLink: "https://www.zarinpal.com/pg/StartPay/" + zarinpalResult.Data.Authority, | ||
}, nil | ||
} | ||
|
||
func (adaptor PaymentAdaptor) VerifyTransaction(ctx context.Context, req payment.TransactionVerificationRequest) (payment.TransactionVerificationResult, error) { | ||
zarinpalRequest := TransactionVerificationRequest{ | ||
MerchantID: adaptor.zarinpal.merchantID, | ||
Authority: req.ServiceOrderID, | ||
Amount: req.PaidAmount, | ||
} | ||
zarinpalResult, err := adaptor.zarinpal.VerifyTransaction(ctx, zarinpalRequest) | ||
if err != nil { | ||
return payment.TransactionVerificationResult{}, err | ||
} | ||
return payment.TransactionVerificationResult{ | ||
TrackID: strconv.Itoa(zarinpalResult.Data.RefId), | ||
PaymentOK: isPaymentOk(zarinpalResult), | ||
}, nil | ||
} | ||
|
||
func isPaymentOk(response TransactionVerificationResult) bool { | ||
if response.Data.Code == 100 || response.Data.Code == 101 { | ||
return true | ||
} | ||
log.WithField("response", response).Info("not ok transaction") | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package zarinpal | ||
|
||
type TransactionCreationRequest struct { | ||
MerchantID string `json:"merchant_id"` | ||
Currency string `json:"currency"` | ||
Description string `json:"description"` | ||
Callback string `json:"callback_url"` | ||
Metadata TransactionCreationRequestMetadata `json:"metadata"` | ||
Amount uint64 `json:"amount"` | ||
} | ||
|
||
type TransactionCreationRequestMetadata struct { | ||
Phone string `json:"mobile"` | ||
Email string `json:"email"` | ||
OrderID string `json:"orderID"` | ||
} | ||
|
||
type TransactionCreationResult struct { | ||
Data struct { | ||
Code int `json:"code"` | ||
Message string `json:"message"` | ||
Authority string `json:"authority"` | ||
FeeType string `json:"fee_type"` | ||
Fee int `json:"fee"` | ||
} `json:"data"` | ||
Errors []interface{} `json:"errors"` | ||
} | ||
|
||
type TransactionVerificationRequest struct { | ||
MerchantID string `json:"merchant_id"` | ||
Authority string `json:"authority"` | ||
Amount uint64 `json:"amount"` | ||
} | ||
|
||
type TransactionVerificationResult struct { | ||
Data struct { | ||
Code int `json:"code"` | ||
Message string `json:"message"` | ||
CardHash string `json:"card_hash"` | ||
CardPan string `json:"card_pan"` | ||
RefId int `json:"ref_id"` | ||
FeeType string `json:"fee_type"` | ||
Fee int `json:"fee"` | ||
} `json:"data"` | ||
Errors []interface{} `json:"errors"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package zarinpal | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"github.com/go-faster/errors" | ||
log "github.com/sirupsen/logrus" | ||
"net/http" | ||
) | ||
|
||
type Zarinpal struct { | ||
merchantID string | ||
} | ||
|
||
func NewZarinpal(merchantID string) Zarinpal { | ||
return Zarinpal{merchantID} | ||
} | ||
|
||
// CreateTransaction will create a new transaction in ID pay and return its result (id and link) | ||
func (zarinpal Zarinpal) CreateTransaction(ctx context.Context, reqBody TransactionCreationRequest) (TransactionCreationResult, error) { | ||
// Create the request | ||
payload, _ := json.Marshal(reqBody) | ||
req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.zarinpal.com/pg/v4/payment/request.json", bytes.NewReader(payload)) | ||
req.Header.Set("Content-Type", "application/json") | ||
// Send the request | ||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return TransactionCreationResult{}, errors.Wrap(err, "cannot send request") | ||
} | ||
// Parse body | ||
var body TransactionCreationResult | ||
err = json.NewDecoder(resp.Body).Decode(&body) | ||
_ = resp.Body.Close() | ||
if err != nil { | ||
return TransactionCreationResult{}, errors.Wrap(err, "cannot parse transaction body") | ||
} | ||
// Check status | ||
if resp.StatusCode/100 == 2 && body.Data.Code == 100 { // 2xx, ok and also the code in body | ||
return body, nil | ||
} else { // fuckup | ||
return TransactionCreationResult{}, errors.Errorf("not 2xx status code: %d (%d) with error message %v", resp.StatusCode, body.Data.Code, body.Errors) | ||
} | ||
} | ||
|
||
// VerifyTransaction will verify a previously made transaction and report errors if there was a problem with it | ||
func (zarinpal Zarinpal) VerifyTransaction(ctx context.Context, reqBody TransactionVerificationRequest) (TransactionVerificationResult, error) { | ||
// Create the request | ||
payload, _ := json.Marshal(reqBody) | ||
req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.zarinpal.com/pg/v4/payment/verify.json", bytes.NewReader(payload)) | ||
req.Header.Set("Content-Type", "application/json") | ||
// Send the request | ||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return TransactionVerificationResult{}, errors.Wrap(err, "cannot send request") | ||
} | ||
// Parse body | ||
var body TransactionVerificationResult | ||
err = json.NewDecoder(resp.Body).Decode(&body) | ||
_ = resp.Body.Close() | ||
if err != nil { | ||
return TransactionVerificationResult{}, errors.Wrap(err, "cannot parse transaction body") | ||
} | ||
// Check status | ||
if resp.StatusCode/100 == 2 { // 2xx, ok and also the code in body | ||
if body.Data.Code == 101 { | ||
log.WithField("resp", body).Warn("double verification") | ||
} | ||
return body, nil | ||
} else { // fuckup | ||
return TransactionVerificationResult{}, errors.Errorf("not 2xx status code: %d (%d) with error message %v", resp.StatusCode, body.Data.Code, body.Errors) | ||
} | ||
} |