igp-sep-example/delivery/httphandler/handler.go

321 lines
7.9 KiB
Go
Raw Permalink 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 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: "پرداخت موفق",
})
}*/