321 lines
7.9 KiB
Go
321 lines
7.9 KiB
Go
package httphandler
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"html/template"
|
||
"log"
|
||
"net/http"
|
||
"net/url"
|
||
"strings"
|
||
|
||
"golang.project/gocasts/ipg-example/service"
|
||
)
|
||
|
||
type Handler struct {
|
||
service service.PaymentService
|
||
templatesPath string
|
||
staticPath string
|
||
}
|
||
|
||
func NewHandler(service service.PaymentService) *Handler {
|
||
return &Handler{
|
||
service: service,
|
||
templatesPath: "./delivery/html/",
|
||
staticPath: "./delivery/static/",
|
||
}
|
||
}
|
||
|
||
func (h *Handler) RegisterRoutes() *http.ServeMux {
|
||
mux := http.NewServeMux()
|
||
|
||
mux.HandleFunc("/", h.homeHandler)
|
||
mux.HandleFunc("/api/init-payment", h.requestPaymentHandler)
|
||
mux.HandleFunc("/api/callback", h.callbackHandler)
|
||
mux.HandleFunc("/result", h.resultHandler)
|
||
|
||
//mux.HandleFunc("/api/verify-payment", h.verifyPaymentHandler)
|
||
|
||
mux.Handle("/static/", http.StripPrefix("/static/",
|
||
http.FileServer(http.Dir(h.staticPath))))
|
||
|
||
return mux
|
||
}
|
||
|
||
func (h *Handler) StartServer(addr string) {
|
||
mux := h.RegisterRoutes()
|
||
log.Printf("Server running on http://localhost%s", addr)
|
||
log.Fatal(http.ListenAndServe(addr, mux))
|
||
}
|
||
|
||
// Params
|
||
type PaymentRequest struct {
|
||
Amount int64 `json:"amount"`
|
||
Phone string `json:"phone"`
|
||
}
|
||
|
||
type PaymentResponse struct {
|
||
Success bool `json:"success"`
|
||
Token string `json:"token,omitempty"`
|
||
PaymentURL string `json:"payment_url,omitempty"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
type VerifyResponse struct {
|
||
Success bool `json:"success"`
|
||
Status string `json:"status"`
|
||
TransactionID string `json:"transaction_id,omitempty"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
type BankCallbackResponse struct {
|
||
Success bool `json:"success"`
|
||
Status string `json:"status"`
|
||
TransactionID string `json:"transaction_id,omitempty"`
|
||
Message string `json:"message"`
|
||
}
|
||
|
||
// Handlers
|
||
func (h *Handler) homeHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.URL.Path != "/" {
|
||
http.NotFound(w, r)
|
||
return
|
||
}
|
||
|
||
tmpl, err := template.ParseFiles(h.templatesPath + "payment.html")
|
||
if err != nil {
|
||
log.Printf("Template error: %v", err)
|
||
http.Error(w, "Template parsing error", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
if err := tmpl.Execute(w, nil); err != nil {
|
||
log.Printf("Template execute error: %v", err)
|
||
}
|
||
}
|
||
|
||
func (h *Handler) requestPaymentHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
var req PaymentRequest
|
||
decoder := json.NewDecoder(r.Body)
|
||
if err := decoder.Decode(&req); err != nil {
|
||
json.NewEncoder(w).Encode(PaymentResponse{
|
||
Success: false,
|
||
Message: "خطا در پارس کردن دادهها",
|
||
})
|
||
return
|
||
}
|
||
|
||
if req.Amount < 1000 {
|
||
json.NewEncoder(w).Encode(PaymentResponse{
|
||
Success: false,
|
||
Message: "حداقل مبلغ ۱۰۰۰ تومان",
|
||
})
|
||
return
|
||
}
|
||
|
||
if req.Phone == "" {
|
||
json.NewEncoder(w).Encode(PaymentResponse{
|
||
Success: false,
|
||
Message: "شماره تلفن الزامی است",
|
||
})
|
||
return
|
||
}
|
||
|
||
token, paymentURL, err := h.service.InitPayment(r.Context(), req.Amount, req.Phone)
|
||
if err != nil {
|
||
json.NewEncoder(w).Encode(PaymentResponse{
|
||
Success: false,
|
||
Message: "خطا در ایجاد پرداخت: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
json.NewEncoder(w).Encode(PaymentResponse{
|
||
Success: true,
|
||
Token: token,
|
||
PaymentURL: paymentURL,
|
||
Message: "پرداخت ایجاد شد",
|
||
})
|
||
}
|
||
|
||
func (h *Handler) callbackHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||
return
|
||
}
|
||
|
||
var state, status, refNum, resNum, traceNo, amount string
|
||
|
||
contentType := r.Header.Get("Content-Type")
|
||
|
||
if strings.Contains(contentType, "application/json") {
|
||
var data map[string]interface{}
|
||
decoder := json.NewDecoder(r.Body)
|
||
if err := decoder.Decode(&data); err != nil {
|
||
log.Printf("Failed to parse JSON: %v", err)
|
||
h.redirectToResult(w, r, "", "NOK", "failed", "", "")
|
||
return
|
||
}
|
||
|
||
if v, ok := data["State"].(string); ok {
|
||
state = v
|
||
}
|
||
if v, ok := data["Status"]; ok {
|
||
status = fmt.Sprintf("%v", v)
|
||
}
|
||
if v, ok := data["RefNum"].(string); ok {
|
||
refNum = v
|
||
}
|
||
if v, ok := data["ResNum"].(string); ok {
|
||
resNum = v
|
||
}
|
||
if v, ok := data["TraceNo"].(string); ok {
|
||
traceNo = v
|
||
}
|
||
if v, ok := data["Amount"]; ok {
|
||
amount = fmt.Sprintf("%v", v)
|
||
}
|
||
} else {
|
||
err := r.ParseForm()
|
||
if err != nil {
|
||
log.Printf("Failed to parse form: %v", err)
|
||
h.redirectToResult(w, r, "", "NOK", "failed", "", "")
|
||
return
|
||
}
|
||
|
||
state = r.FormValue("State")
|
||
status = r.FormValue("Status")
|
||
refNum = r.FormValue("RefNum")
|
||
resNum = r.FormValue("ResNum")
|
||
traceNo = r.FormValue("TraceNo")
|
||
amount = r.FormValue("Amount")
|
||
}
|
||
|
||
log.Printf("Bank Callback Received:")
|
||
log.Printf(" State: %s", state)
|
||
log.Printf(" Status: %s", status)
|
||
log.Printf(" RefNum: %s", refNum)
|
||
log.Printf(" ResNum: %s", resNum)
|
||
log.Printf(" TraceNo: %s", traceNo)
|
||
log.Printf(" Amount: %s", amount)
|
||
|
||
isSuccess := (state == "OK" || status == "0")
|
||
|
||
if !isSuccess {
|
||
// payment failed
|
||
h.redirectToResult(w, r, "", state, "failed", refNum, amount)
|
||
return
|
||
}
|
||
|
||
// verify transaction
|
||
transactionID, err := h.service.VerifyPayment(r.Context(), resNum, refNum, traceNo, amount)
|
||
if err != nil {
|
||
log.Printf("❌ Verify failed: %v", err)
|
||
h.redirectToResult(w, r, "", state, "verify_failed", refNum, amount)
|
||
return
|
||
}
|
||
|
||
log.Printf("Payment verified successfully. TransactionID: %s", transactionID)
|
||
|
||
h.redirectToResult(w, r, transactionID, state, "success", refNum, amount)
|
||
}
|
||
|
||
func (h *Handler) resultHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.URL.Path != "/result" {
|
||
http.NotFound(w, r)
|
||
return
|
||
}
|
||
|
||
if r.Method == http.MethodPost {
|
||
if err := r.ParseForm(); err != nil {
|
||
http.Error(w, "خطا در پارس کردن دادهها", http.StatusBadRequest)
|
||
return
|
||
}
|
||
}
|
||
|
||
state := r.FormValue("state")
|
||
status := r.FormValue("status")
|
||
refNum := r.FormValue("refNum")
|
||
amount := r.FormValue("amount")
|
||
transactionID := r.FormValue("transactionId")
|
||
|
||
isSuccess := (state == "OK" || status == "success")
|
||
|
||
data := map[string]interface{}{
|
||
"State": state,
|
||
"Status": status,
|
||
"IsSuccess": isSuccess,
|
||
"RefNum": refNum,
|
||
"Amount": amount,
|
||
"TransactionID": transactionID,
|
||
}
|
||
|
||
tmpl, err := template.ParseFiles(h.templatesPath + "result.html")
|
||
if err != nil {
|
||
log.Printf("Template error: %v", err)
|
||
http.Error(w, "Template parsing error", http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
if err := tmpl.Execute(w, data); err != nil {
|
||
log.Printf("Template execute error: %v", err)
|
||
}
|
||
}
|
||
|
||
func (h *Handler) redirectToResult(w http.ResponseWriter, r *http.Request, transactionID, state, status, refNum, amount string) {
|
||
query := fmt.Sprintf("state=%s&status=%s", state, status)
|
||
|
||
if transactionID != "" {
|
||
query += "&transactionId=" + url.QueryEscape(transactionID)
|
||
}
|
||
if refNum != "" {
|
||
query += "&refNum=" + url.QueryEscape(refNum)
|
||
}
|
||
if amount != "" {
|
||
query += "&amount=" + url.QueryEscape(amount)
|
||
}
|
||
|
||
redirectURL := "/result?" + query
|
||
http.Redirect(w, r, redirectURL, http.StatusFound)
|
||
}
|
||
|
||
/*func (h *Handler) verifyPaymentHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
token := r.URL.Query().Get("token")
|
||
status := r.URL.Query().Get("status")
|
||
|
||
if token == "" || status == "" {
|
||
json.NewEncoder(w).Encode(VerifyResponse{
|
||
Success: false,
|
||
Status: "failed",
|
||
Message: "پارامترهای نامعتبر",
|
||
})
|
||
return
|
||
}
|
||
|
||
transactionID, err := h.service.VerifyPayment(r.Context(), token, status, "", "")
|
||
if err != nil || status != "OK" {
|
||
json.NewEncoder(w).Encode(VerifyResponse{
|
||
Success: false,
|
||
Status: "failed",
|
||
Message: "پرداخت ناموفق",
|
||
})
|
||
return
|
||
}
|
||
|
||
json.NewEncoder(w).Encode(VerifyResponse{
|
||
Success: true,
|
||
Status: "success",
|
||
TransactionID: transactionID,
|
||
Message: "پرداخت موفق",
|
||
})
|
||
}*/
|