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: "پرداخت موفق", }) }*/