183 lines
5.2 KiB
Go
183 lines
5.2 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log/slog"
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
type SepTokenResponse struct {
|
|
Status int `json:"status"`
|
|
ErrorCode string `json:"errorCode"`
|
|
ErrorDesc string `json:"errorDesc"`
|
|
Token string `json:"token"`
|
|
}
|
|
type VerifyTransactionResponse struct {
|
|
TransactionDetail TransactionDetail `json:"TransactionDetail"`
|
|
ResultCode int `json:"ResultCode"`
|
|
ResultDescription string `json:"ResultDescription"`
|
|
Success bool `json:"Success"`
|
|
}
|
|
|
|
type TransactionDetail struct {
|
|
RRN string `json:"RRN"`
|
|
RefNum string `json:"RefNum"`
|
|
MaskedPan string `json:"MaskedPan"`
|
|
HashedPan string `json:"HashedPan"`
|
|
TerminalNumber int `json:"TerminalNumber"`
|
|
OrginalAmount int64 `json:"OrginalAmount"`
|
|
AffectiveAmount int64 `json:"AffectiveAmount"`
|
|
StraceDate string `json:"StraceDate"`
|
|
StraceNo string `json:"StraceNo"`
|
|
}
|
|
|
|
type ReverseTransactionResponse struct {
|
|
TransactionDetail TransactionDetail `json:"TransactionDetail"`
|
|
ResultCode int `json:"ResultCode"`
|
|
ResultDescription string `json:"ResultDescription"`
|
|
Success bool `json:"Success"`
|
|
}
|
|
|
|
func (p *PaymentService) RealInitialize(ctx context.Context, credential, phone, resNum string, amount int64) (*SepTokenResponse, error) {
|
|
description := p.generateDescription()
|
|
|
|
message := SepTokenRequest{
|
|
Action: "token",
|
|
TerminalId: credential,
|
|
Amount: amount,
|
|
ResNum: resNum,
|
|
RedirectUrl: p.Cfg.CallbackURL,
|
|
CellNumber: phone,
|
|
TokenExpiryInMin: 20,
|
|
}
|
|
|
|
if len(description) > 0 {
|
|
if len(description) <= 50 {
|
|
message.ResNum1 = description
|
|
} else if len(description) <= 100 {
|
|
message.ResNum1 = description[:50]
|
|
message.ResNum2 = description[50:]
|
|
} else if len(description) <= 150 {
|
|
message.ResNum1 = description[:50]
|
|
message.ResNum2 = description[50:100]
|
|
message.ResNum3 = description[100:]
|
|
} else {
|
|
message.ResNum1 = description[:50]
|
|
message.ResNum2 = description[50:100]
|
|
message.ResNum3 = description[100:150]
|
|
message.ResNum4 = description[150:]
|
|
}
|
|
}
|
|
|
|
bodyBytes, err := json.Marshal(message)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal error: %v", err)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", p.Cfg.PaymentGetToken, bytes.NewReader(bodyBytes))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request creation error: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
slog.Info("SEP Token Request", "endpoint", p.Cfg.PaymentGetToken, "body", message)
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request error: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
responseBody, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read body error: %v", err)
|
|
}
|
|
|
|
slog.Info("SEP Token Response", "status_code", resp.StatusCode, "body", string(responseBody))
|
|
|
|
var parsed SepTokenResponse
|
|
if err := json.Unmarshal(responseBody, &parsed); err != nil {
|
|
return nil, fmt.Errorf("unmarshal error: %v", err)
|
|
}
|
|
|
|
if parsed.Status == 1 && parsed.Token != "" {
|
|
slog.Info("sep payment initialized", "token", parsed.Token)
|
|
return &parsed, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("sep init error (code %s): %s", parsed.ErrorCode, parsed.ErrorDesc)
|
|
}
|
|
|
|
func (p *PaymentService) RealVerifyTransaction(ctx context.Context, refNum string) (*VerifyTransactionResponse, error) {
|
|
terminalNumber, err := strconv.Atoi(p.Cfg.Credential)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("خطا در تبدیل TerminalId: %v", err)
|
|
}
|
|
|
|
requestBody := map[string]interface{}{
|
|
"RefNum": refNum,
|
|
"TerminalNumber": terminalNumber,
|
|
}
|
|
|
|
bodyBytes, err := json.Marshal(requestBody)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal error: %v", err)
|
|
}
|
|
|
|
verifyURL := p.Cfg.PaymentVerifyURL
|
|
if verifyURL == "" {
|
|
verifyURL = "https://sep.shaparak.ir/verifyTxnRandomSessionkey/ipg/VerifyTransaction"
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", verifyURL, bytes.NewReader(bodyBytes))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request creation error: %v", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
slog.Info("SEP VerifyTransaction Request",
|
|
"endpoint", verifyURL,
|
|
"refNum", refNum,
|
|
"terminalNumber", terminalNumber)
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request error: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read body error: %v", err)
|
|
}
|
|
|
|
slog.Info("SEP VerifyTransaction Response",
|
|
"status_code", resp.StatusCode,
|
|
"body", string(respBytes))
|
|
|
|
var verifyResp VerifyTransactionResponse
|
|
if err := json.Unmarshal(respBytes, &verifyResp); err != nil {
|
|
return nil, fmt.Errorf("unmarshal error: %v", err)
|
|
}
|
|
|
|
slog.Info("VerifyTransaction Result",
|
|
"success", verifyResp.Success,
|
|
"resultCode", verifyResp.ResultCode,
|
|
"resultDescription", verifyResp.ResultDescription)
|
|
|
|
return &verifyResp, nil
|
|
}
|
|
|
|
func (p *PaymentService) generateDescription() string {
|
|
description := "سفارش از درگاه"
|
|
if p.IPGTransaction != nil {
|
|
description += " : " + p.IPGTransaction.ID
|
|
}
|
|
return description
|
|
}
|