igp-sep-example/service/service.go

155 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"log/slog"
"strconv"
"time"
)
type Config struct {
PaymentGetToken string // آدرس دریافت توکن
PaymentVerifyURL string // آدرس تأیید تراکنش
PaymentReverseURL string // آدرس برگشت تراکنش
CallbackURL string // آدرس بازگشت
Credential string // TerminalId
}
type IPGTransaction struct {
ID string `json:"id"`
IPGType string `json:"ipg_type"`
GatewayMeta map[string]string `json:"gateway_meta,omitempty"`
Token string `json:"token"`
Amount float64 `json:"amount"`
CreatedAt time.Time `json:"created_at"`
}
type PaymentService struct {
IPGTransaction *IPGTransaction
Cfg Config
}
type InitPaymentResponse struct {
Success bool `json:"success"`
GatewayToken string `json:"gateway_token"`
RedirectUrl string `json:"redirect_url"`
IPGTransactionId string `json:"ipg_transaction_id"`
Message string `json:"message"`
Time time.Time `json:"time"`
}
// SEP Request/Response
type SepTokenRequest struct {
Action string `json:"action"`
TerminalId string `json:"TerminalId"`
Amount int64 `json:"Amount"`
ResNum string `json:"ResNum"`
RedirectUrl string `json:"RedirectUrl"`
CellNumber string `json:"CellNumber"`
TokenExpiryInMin int `json:"TokenExpiryInMin,omitempty"`
ResNum1 string `json:"ResNum1,omitempty"`
ResNum2 string `json:"ResNum2,omitempty"`
ResNum3 string `json:"ResNum3,omitempty"`
ResNum4 string `json:"ResNum4,omitempty"`
}
func NewPaymentService(cfg Config) PaymentService {
return PaymentService{
Cfg: cfg,
}
}
func (p *PaymentService) InitPayment(ctx context.Context, amount int64, phone string) (string, string, error) {
res, err := p.startPayment(ctx, amount, phone)
if err != nil {
return "", "", err
}
return res.GatewayToken, res.RedirectUrl, nil
}
func (p *PaymentService) startPayment(ctx context.Context, amount int64, phone string) (*InitPaymentResponse, error) {
if amount < 1000 {
return nil, fmt.Errorf("حداقل مبلغ ۱۰۰۰ تومان است")
}
resNum := GenerateUniqueString()
ipgTx := &IPGTransaction{
ID: resNum,
IPGType: "sep",
GatewayMeta: map[string]string{
"callback_url": p.Cfg.CallbackURL,
},
Amount: float64(amount),
CreatedAt: time.Now(),
}
p.IPGTransaction = ipgTx
initRes, err := p.RealInitialize(ctx, p.Cfg.Credential, phone, resNum, amount)
if err != nil {
slog.Error("failed to init payment", "error", err)
return nil, err
}
redirectUrl := fmt.Sprintf("https://sep.shaparak.ir/OnlinePG/SendToken?token=%s", initRes.Token)
return &InitPaymentResponse{
Success: true,
GatewayToken: initRes.Token,
RedirectUrl: redirectUrl,
IPGTransactionId: ipgTx.ID,
Time: time.Now(),
Message: "پرداخت ایجاد شد",
}, nil
}
func (p *PaymentService) VerifyPayment(ctx context.Context, resNum, refNum, traceNo, amount string) (string, error) {
if resNum == "" {
return "", fmt.Errorf("شماره سفارش نامعتبر")
}
expectedAmount, err := strconv.ParseInt(amount, 10, 64)
if err != nil {
return "", fmt.Errorf("مبلغ نامعتبر: %v", err)
}
verifyResp, err := p.RealVerifyTransaction(ctx, refNum)
if err != nil {
return "", fmt.Errorf("خطا در تأیید تراکنش: %v", err)
}
if !verifyResp.Success {
return "", fmt.Errorf("تأیید تراکنش ناموفق: %s", verifyResp.ResultDescription)
}
actualAmount := verifyResp.TransactionDetail.OrginalAmount
if actualAmount != expectedAmount {
slog.Error("amount mismatch",
"expected", expectedAmount,
"actual", actualAmount)
return "", fmt.Errorf("مبلغ تراکنش مغایرت دارد")
}
trackingCode := verifyResp.TransactionDetail.StraceNo
if trackingCode == "" {
trackingCode = verifyResp.TransactionDetail.RRN
}
slog.Info("payment verified successfully",
"refNum", refNum,
"trackingCode", trackingCode,
"amount", actualAmount)
return trackingCode, nil
}
func GenerateUniqueString() string {
timestamp := time.Now().UnixNano()
randomBytes := make([]byte, 8)
rand.Read(randomBytes)
return fmt.Sprintf("%d-%s", timestamp, hex.EncodeToString(randomBytes))
}