diff --git a/cmd/events_resend.go b/cmd/events_resend.go index 3ba7398..b621df1 100644 --- a/cmd/events_resend.go +++ b/cmd/events_resend.go @@ -39,18 +39,14 @@ func resendEvent(id string) error { return fmt.Errorf("event %s found but has no raw payload stored", id) } - defaultURL := "http://localhost:3000/webhooks/abacatepay" - if resendForwardURL == "" { - if entry.URL != "" { - defaultURL = entry.URL - } - - if inputErr := style.Input("Forward event to", defaultURL, &resendForwardURL, nil); inputErr != nil { - return inputErr - } - if resendForwardURL == "" { - resendForwardURL = defaultURL - } + defaultURL := utils.DefaultForwardURL + if entry.URL != "" { + defaultURL = entry.URL + } + + url, err := utils.GetForwardURL(resendForwardURL, defaultURL) + if err != nil { + return err } deps := utils.SetupDependencies(Local, Verbose) @@ -60,14 +56,14 @@ func resendEvent(id string) error { signature := crypto.SignWebhookPayload(secret, timestamp, []byte(entry.RawMessage)) style.LogSigningSecret(secret) - fmt.Printf("Resending event %s to %s...\n", id, resendForwardURL) + fmt.Printf("Resending event %s to %s...\n", id, url) startTime := time.Now() resp, err := deps.Client.R(). SetHeader("Content-Type", "application/json"). SetHeader("X-Abacate-Signature", fmt.Sprintf("t=%d,v1=%s", timestamp, signature)). SetBody(entry.RawMessage). - Post(resendForwardURL) + Post(url) duration := time.Since(startTime) diff --git a/cmd/listen.go b/cmd/listen.go index 6c20581..7405cf4 100644 --- a/cmd/listen.go +++ b/cmd/listen.go @@ -6,7 +6,6 @@ import ( "os/signal" "syscall" - "abacatepay-cli/internal/style" "abacatepay-cli/internal/utils" "github.com/spf13/cobra" @@ -38,27 +37,19 @@ func listen(cmd *cobra.Command) error { return err } - defaultURL := "http://localhost:3000/webhooks/abacatepay" - if !cmd.Flags().Changed("forward-to") { - err := style.Input("Forward events to", defaultURL, &forwardURL, nil) - if err != nil { - return err - } - - if forwardURL == "" { - forwardURL = defaultURL - } + url, err := utils.GetForwardURL(forwardURL, utils.DefaultForwardURL) + if err != nil { + return err } ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - defer cancel() params := &utils.StartListenerParams{ Context: ctx, Config: deps.Config, Client: deps.Client, - ForwardURL: forwardURL, + ForwardURL: url, Store: deps.Store, Token: deps.Config.TokenKey, Version: cmd.Root().Version, diff --git a/cmd/payments_check.go b/cmd/payments_check.go index 6902eda..dbee747 100644 --- a/cmd/payments_check.go +++ b/cmd/payments_check.go @@ -2,7 +2,6 @@ package cmd import ( "abacatepay-cli/internal/payments" - "abacatepay-cli/internal/utils" "github.com/spf13/cobra" ) @@ -21,12 +20,7 @@ func init() { } func check(paymentID string) error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - return err - } - - pixService := payments.New(deps.Client, deps.Config.APIBaseURL) - - return pixService.CheckPixQRCode(paymentID) + return payments.ExecutePaymentAction(Local, Verbose, func(s *payments.Service) error { + return s.CheckPixQRCode(paymentID) + }) } diff --git a/cmd/payments_create.go b/cmd/payments_create.go index 8cf7e6d..aa56125 100644 --- a/cmd/payments_create.go +++ b/cmd/payments_create.go @@ -57,7 +57,7 @@ func createPayment(method string) error { } } - service := payments.New(deps.Client, deps.Config.APIBaseURL) + service := payments.New(deps.Client, deps.Config.APIBaseURL, Verbose) switch method { case "pix", "pix_qrcode": diff --git a/cmd/payments_simulate.go b/cmd/payments_simulate.go index 5b6b9e2..1be7ac6 100644 --- a/cmd/payments_simulate.go +++ b/cmd/payments_simulate.go @@ -2,7 +2,6 @@ package cmd import ( "abacatepay-cli/internal/payments" - "abacatepay-cli/internal/utils" "github.com/spf13/cobra" ) @@ -21,12 +20,7 @@ func init() { } func simulate(paymentID string) error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - return err - } - - pixService := payments.New(deps.Client, deps.Config.APIBaseURL) - - return pixService.SimulatePixQRCodePayment(paymentID, false) + return payments.ExecutePaymentAction(Local, Verbose, func(s *payments.Service) error { + return s.SimulatePixQRCodePayment(paymentID, false) + }) } diff --git a/cmd/trigger.go b/cmd/trigger.go index 56cf2f5..28e38d8 100644 --- a/cmd/trigger.go +++ b/cmd/trigger.go @@ -34,7 +34,7 @@ func trigger(evt string) error { } func handleEvent(deps *utils.Dependencies, evt string) error { - service := payments.New(deps.Client, deps.Config.APIBaseURL) + service := payments.New(deps.Client, deps.Config.APIBaseURL, Verbose) switch evt { case "billing.paid": diff --git a/internal/auth/login.go b/internal/auth/login.go index 4774855..f2f678b 100644 --- a/internal/auth/login.go +++ b/internal/auth/login.go @@ -28,11 +28,12 @@ type LoginParams struct { } func Login(params *LoginParams) error { - if params.ProfileName == "" { - params.ProfileName = fmt.Sprintf("profile-%d", time.Now().Unix()%10000) + if params.APIKey != "" { + return loginWithAPIKey(params) } - if params.APIKey != "" { + if params.Config.APIBaseURL == "http://191.252.202.128:8080" { + params.APIKey = "mock_token_local_dev" return loginWithAPIKey(params) } diff --git a/internal/client/client.go b/internal/client/client.go index a5a71dd..399ce11 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -9,6 +9,5 @@ import ( func New(cfg *config.Config) *resty.Client { return resty.New(). SetTimeout(cfg.HTTPTimeout). - SetHeader("User-Agent", "abacatepay-cli/1.0"). - SetDebug(cfg.Verbose) + SetHeader("User-Agent", "abacatepay-cli/1.0") } diff --git a/internal/config/config.go b/internal/config/config.go index 901a59c..35c361c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -18,7 +18,7 @@ func Default() *Config { WebSocketBaseURL: "wss://ws.abacatepay.com/ws", ServiceName: "abacatepay-cli", TokenKey: "auth-token", - HTTPTimeout: 10 * time.Second, + HTTPTimeout: 15 * time.Second, DefaultForwardURL: "http://localhost:3000/webhooks", Verbose: false, } diff --git a/internal/output/output.go b/internal/output/output.go index 00617f4..0b39e3e 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -1,10 +1,7 @@ package output import ( - "encoding/json" "fmt" - "log/slog" - "os" "sync" "abacatepay-cli/internal/style" @@ -110,19 +107,11 @@ func printJSON(r Result) { } } - encoder := json.NewEncoder(os.Stdout) - encoder.SetIndent("", " ") - if err := encoder.Encode(data); err != nil { - slog.Debug("failed to encode JSON output", "error", err) - } + style.PrintJSON(data) } func printJSONError(msg string) { - encoder := json.NewEncoder(os.Stdout) - encoder.SetIndent("", " ") - if err := encoder.Encode(map[string]any{"error": msg}); err != nil { - slog.Debug("failed to encode JSON error output", "error", err) - } + style.PrintJSON(map[string]any{"error": msg}) } func printText(r Result) { @@ -165,19 +154,15 @@ func printProfilesJSON(profiles []Profile, activeProfile string) { profileData := make([]map[string]any, 0, len(profiles)) for _, p := range profiles { profileData = append(profileData, map[string]any{ - "name": p.Name, - "active": p.Active, + "name": p.Name, + "key": formatShortToken(p.Token), }) } - encoder := json.NewEncoder(os.Stdout) - encoder.SetIndent("", " ") - if err := encoder.Encode(map[string]any{ + style.PrintJSON(map[string]any{ "profiles": profileData, "active": activeProfile, - }); err != nil { - slog.Debug("failed to encode profiles JSON output", "error", err) - } + }) } func printProfilesTable(profiles []Profile) { @@ -187,7 +172,7 @@ func printProfilesTable(profiles []Profile) { shortKey := formatShortToken(p.Token) activeMarker := "" if p.Active { - activeMarker = "Yes" + activeMarker = "🥑" } rows = append(rows, []string{p.Name, shortKey, activeMarker}) } diff --git a/internal/payments/payments.go b/internal/payments/payments.go index 267264b..312a654 100644 --- a/internal/payments/payments.go +++ b/internal/payments/payments.go @@ -6,6 +6,7 @@ import ( "net/http" "abacatepay-cli/internal/output" + "abacatepay-cli/internal/style" "abacatepay-cli/internal/types" "github.com/go-resty/resty/v2" @@ -14,16 +15,35 @@ import ( type Service struct { Client *resty.Client BaseURL string + Verbose bool } -func New(client *resty.Client, baseURL string) *Service { +func New(client *resty.Client, baseURL string, verbose bool) *Service { return &Service{ Client: client, BaseURL: baseURL, + Verbose: verbose, } } func (s *Service) executeRequest(req *resty.Request, method, url string, result any) error { + if s.Verbose { + fmt.Printf("Request: %s %s\n", method, url) + if body := req.Body; body != nil { + if b, ok := body.([]byte); ok { + var pretty any + if err := json.Unmarshal(b, &pretty); err == nil { + style.PrintJSON(pretty) + } else { + fmt.Println(string(b)) + } + } else { + style.PrintJSON(body) + } + } + fmt.Println() + } + var resp *resty.Response var err error @@ -40,6 +60,10 @@ func (s *Service) executeRequest(req *resty.Request, method, url string, result return fmt.Errorf("failed to send request: %w", err) } + if s.Verbose { + fmt.Printf("Response: %s\n", resp.Status()) + } + if resp.IsError() { return s.handleAPIError(resp) } @@ -48,6 +72,11 @@ func (s *Service) executeRequest(req *resty.Request, method, url string, result return fmt.Errorf("failed to parse response: %w", err) } + if s.Verbose { + style.PrintJSON(result) + fmt.Println() + } + return nil } diff --git a/internal/payments/utils.go b/internal/payments/utils.go new file mode 100644 index 0000000..d97b00a --- /dev/null +++ b/internal/payments/utils.go @@ -0,0 +1,13 @@ +package payments + +import "abacatepay-cli/internal/utils" + +func ExecutePaymentAction(local, verbose bool, action func(*Service) error) error { + deps, err := utils.SetupClient(local, verbose) + if err != nil { + return err + } + + pixService := New(deps.Client, deps.Config.APIBaseURL, verbose) + return action(pixService) +} diff --git a/internal/style/style.go b/internal/style/style.go index 13be235..5ce9d56 100644 --- a/internal/style/style.go +++ b/internal/style/style.go @@ -93,7 +93,6 @@ func PrintTable(headers []string, rows [][]string) { t.StyleFunc(func(row, col int) lipgloss.Style { if row == 0 { return lipgloss.NewStyle(). - Foreground(Palette.Green). Bold(true). Align(lipgloss.Center) } diff --git a/internal/utils/network.go b/internal/utils/network.go index eefca24..05ec840 100644 --- a/internal/utils/network.go +++ b/internal/utils/network.go @@ -3,11 +3,55 @@ package utils import ( "fmt" "net" + "net/url" "os/exec" "runtime" "time" + + "abacatepay-cli/internal/style" ) +const DefaultForwardURL = "http://localhost:3000/webhooks/abacatepay" + +func validateForwardURL(s string) error { + u, err := url.ParseRequestURI(s) + if err != nil { + return fmt.Errorf("invalid URL format: %w", err) + } + if u.Scheme != "http" && u.Scheme != "https" { + return fmt.Errorf("URL must start with http:// or https://") + } + if u.Host == "" { + return fmt.Errorf("URL must include a valid host") + } + return nil +} + +func promptForwardURL(defaultURL string, result *string) error { + if err := style.Input("Forward events to", defaultURL, result, validateForwardURL); err != nil { + return err + } + if *result == "" { + *result = defaultURL + } + return nil +} + +func GetForwardURL(flagValue, defaultURL string) (string, error) { + if flagValue != "" { + if err := validateForwardURL(flagValue); err != nil { + return "", err + } + return flagValue, nil + } + + var result string + if err := promptForwardURL(defaultURL, &result); err != nil { + return "", err + } + return result, nil +} + func IsOnline() bool { const googleDNS = "8.8.8.8:53" const timeout = 2 * time.Second