From e30fdc1e86b354570a2c57f18c7b79ae954ab84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caro=C3=A7o?= Date: Tue, 9 Jun 2026 00:40:13 +0000 Subject: [PATCH] fix(cli): align install and v2 dev workflows --- .goreleaser.yaml | 2 + Makefile | 6 +- PR_DESCRIPTION.md | 44 +++---- README.md | 120 +++++++++---------- cmd/abacatepay/main.go | 24 ++++ cmd/docs.go | 20 ---- cmd/events.go | 13 -- cmd/events_resend.go | 91 -------------- cmd/events_sample.go | 44 ------- cmd/init.go | 121 ------------------- cmd/listen.go | 2 +- cmd/login.go | 4 +- cmd/logout.go | 6 +- cmd/logs.go | 14 --- cmd/logs_list.go | 94 --------------- cmd/logs_tail.go | 56 --------- cmd/payments.go | 3 +- cmd/payments_check.go | 26 ---- cmd/payments_create.go | 96 --------------- cmd/payments_simulate.go | 13 +- cmd/profile.go | 16 --- cmd/profile_delete.go | 50 -------- cmd/profile_list.go | 63 ---------- cmd/root.go | 11 +- cmd/status.go | 49 -------- cmd/switch.go | 46 -------- cmd/trigger.go | 89 -------------- cmd/update.go | 52 -------- cmd/verify.go | 138 ---------------------- cmd/whoami.go | 56 --------- go.mod | 21 +--- go.sum | 44 ------- internal/auth/api.go | 39 +++++- internal/auth/login.go | 28 +++-- internal/client/client.go | 4 +- internal/config/config.go | 10 +- internal/mock/events.go | 72 ----------- internal/mock/helpers.go | 42 ------- internal/mock/payments.go | 54 --------- internal/output/output.go | 2 +- internal/payments/checkout.go | 34 ------ internal/payments/payments.go | 6 +- internal/payments/pix_qrcode.go | 76 +++--------- internal/payments/utils.go | 2 +- internal/prompts/prompts.go | 82 ------------- internal/scaffold/config.go | 68 ----------- internal/scaffold/fs.go | 72 ----------- internal/scaffold/git.go | 24 ---- internal/scaffold/package.go | 117 ------------------ internal/scaffold/scaffold.go | 190 ------------------------------ internal/types/types.go | 70 +++++------ internal/utils/listener.go | 2 +- internal/utils/network.go | 2 +- internal/utils/utils.go | 10 +- internal/version/update.go | 43 ------- internal/webhook/base_listener.go | 4 +- internal/webhook/listener.go | 6 +- internal/webhook/tail.go | 6 +- internal/webhook/webhook.go | 2 +- internal/ws/connection.go | 2 +- main.go | 4 +- 61 files changed, 255 insertions(+), 2252 deletions(-) create mode 100644 cmd/abacatepay/main.go delete mode 100644 cmd/docs.go delete mode 100644 cmd/events.go delete mode 100644 cmd/events_resend.go delete mode 100644 cmd/events_sample.go delete mode 100644 cmd/init.go delete mode 100644 cmd/logs.go delete mode 100644 cmd/logs_list.go delete mode 100644 cmd/logs_tail.go delete mode 100644 cmd/payments_check.go delete mode 100644 cmd/payments_create.go delete mode 100644 cmd/profile.go delete mode 100644 cmd/profile_delete.go delete mode 100644 cmd/profile_list.go delete mode 100644 cmd/status.go delete mode 100644 cmd/switch.go delete mode 100644 cmd/trigger.go delete mode 100644 cmd/update.go delete mode 100644 cmd/verify.go delete mode 100644 cmd/whoami.go delete mode 100644 internal/mock/events.go delete mode 100644 internal/mock/helpers.go delete mode 100644 internal/mock/payments.go delete mode 100644 internal/payments/checkout.go delete mode 100644 internal/prompts/prompts.go delete mode 100644 internal/scaffold/config.go delete mode 100644 internal/scaffold/fs.go delete mode 100644 internal/scaffold/git.go delete mode 100644 internal/scaffold/package.go delete mode 100644 internal/scaffold/scaffold.go delete mode 100644 internal/version/update.go diff --git a/.goreleaser.yaml b/.goreleaser.yaml index c2014e8..07dc601 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -8,6 +8,7 @@ before: builds: - id: abacatepay + main: ./cmd/abacatepay binary: abacatepay env: - CGO_ENABLED=0 @@ -23,6 +24,7 @@ builds: # Alias (abkt) - id: abkt + main: ./cmd/abacatepay binary: abkt env: - CGO_ENABLED=0 diff --git a/Makefile b/Makefile index 0b337de..a3fcccc 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ .PHONY: build test lint clean coverage install -BINARY=abacate +BINARY=abacatepay build: - go build -o $(BINARY) . + go build -o $(BINARY) ./cmd/abacatepay test: go test -v -race ./... @@ -12,7 +12,7 @@ lint: golangci-lint run install: - go install . + go install ./cmd/abacatepay check: test lint diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md index 40a4f52..67849ba 100644 --- a/PR_DESCRIPTION.md +++ b/PR_DESCRIPTION.md @@ -1,37 +1,23 @@ ## Summary -Adds `init` command that initializes new projects with interactive scaffolding, allowing choice between frameworks (Next.js/Elysia), linters (ESLint/Biome), and optional BetterAuth configuration. +- Fixes Go installation by using the canonical module path and adding `cmd/abacatepay` so Go users can install the expected `abacatepay` binary. +- Narrows the CLI to the requested supported surface: auth, webhook listening, and dev-mode payment simulation. +- Updates payment simulation to API v2: `POST /v2/transparents/simulate-payment?id=`. +- Removes unsupported commands/features from the current codebase until there is documented API v2 parity. +- Updates README installation and usage docs. -## Related Issue +Closes #32. -Closes (N/A) +## Validation -## Why +- `go mod tidy` +- `go test ./...` +- `go build ./...` +- `GOBIN=/tmp/abacatepay-cli-bin go install .` and root CLI help smoke test +- `GOBIN=/tmp/abacatepay-bin go install ./cmd/abacatepay` and `abacatepay` help smoke test -The CLI needed a simple way to create new projects integrated with AbacatePay. Before, developers had to manually clone templates and configure everything by hand. The `init` command automates this entire process with an interactive onboarding flow. +## Notes -## What changed +The `--local` flag is kept as a backwards-compatible no-op because API v2 uses the same public endpoint for production and dev mode; the API key determines the environment. -- Created `cmd/init.go` with interactive onboarding flow using prompts from the `style` package -- Created `internal/scaffold/config.go` with `Config` structure and validation methods -- Created `internal/scaffold/git.go` for repository clone operations -- Created `internal/scaffold/package.go` for `package.json` manipulation and merging -- Created `internal/scaffold/fs.go` for file and directory copy operations -- Created `internal/scaffold/scaffold.go` with scaffolding orchestration via `ProjectBuilder` -- Implemented layer-based composition system that combines base templates + linters + features (BetterAuth) - -## Breaking changes - -- [ ] Yes -- [X] No - -## Checklist - -- [ ] Docs updated -- [X] CI passing -- [X] I followed the CONTRIBUTING guidelines -- [X] I added or updated tests (if applicable) - -## Additional context - -The command uses the `github.com/albuquerquesz/abacatepay-templates` repository as the source for templates. The layer-based composition architecture allows adding new frameworks, linters, and features without creating a combinatorial explosion of templates. +The old root Go install path still works and produces `abacatepay-cli`; the documented Go path now installs `abacatepay`. diff --git a/README.md b/README.md index 571b7cb..5130253 100644 --- a/README.md +++ b/README.md @@ -1,125 +1,117 @@

AbacatePay CLI

- CLI oficial do AbacatePay para desenvolvimento local, webhooks e testes rápidos via terminal. + CLI oficial da AbacatePay para autenticação, recebimento de webhooks e simulação de pagamentos em modo de desenvolvimento.

Instalação • - Uso • + Uso rápidoAutenticação • - Ambientes + Webhooks • + Pagamentos em dev mode

-

Instalação

+## Instalação -

Go (recomendado)

+### Go ```bash -go install github.com/AbacatePay/abacatepay-cli@latest +go install github.com/AbacatePay/abacatepay-cli/cmd/abacatepay@latest ``` -

O binário automáticamente será instalado como abacatepay com um aliás abkt.

+Isso instala o binário `abacatepay`. -

Homebrew (macOS / Linux)

+O comando antigo também funciona, mas instala o binário com o nome do repositório: ```bash -brew install --build-from-source github.com/AbacatePay/abacatepay-cli +go install github.com/AbacatePay/abacatepay-cli@latest +# binário: abacatepay-cli ``` -

Uso Rápido

+### Binários prontos -```bash -abacatepay login -``` +Os releases publicam binários para Linux, macOS e Windows nas arquiteturas `amd64` e `arm64`. -

Após a autenticação (OAuth Device Flow), você deve informar a URL do seu servidor local, então a CLI encaminhará todos os webhooks para você.

- -

Todos os comandos podem ser usados com a seguinte sintaxe:

+## Uso rápido ```bash -abacatepay [...flags] [...args] +abacatepay login --key "$ABACATEPAY_API_KEY" +abacatepay listen --forward-to http://localhost:3000/webhooks/abacatepay +abacatepay payments simulate ``` -

Use a flag -h para obter informações detalhadas sobre cada comando.

- -

Ambientes

+Use `abacatepay -h` para ver as flags de cada comando. -

Atualmente a CLI suporta dois ambientes, o de produção (Padrão) e teste.

+## Autenticação -

Produção (Padrão)

+A CLI aceita login por chave de API ou pelo fluxo de device login: ```bash -# API: https://api.abacatepay.com -# WebSocket: wss://ws.abacatepay.com/ws +# Recomendado para automações e ambientes sem navegador +abacatepay login --key "$ABACATEPAY_API_KEY" +# Fluxo interativo pelo navegador abacatepay login ``` -

Servidor de Teste

+A chave fica salva no keyring nativo do sistema operacional. Se nenhum nome for informado com `--name`, a CLI usa o perfil `default`. -

Para usar o modo de desenvolvimento, use a flag -l

+A API v2 usa o mesmo endpoint público para produção e desenvolvimento. O ambiente é determinado pela chave utilizada: chaves de dev mode simulam transações; chaves de produção operam em produção. +## Webhooks -```bash -# API: http://191.252.202.128:8080 -# WebSocket: ws://191.252.202.128:8080/ws +Receba eventos da AbacatePay e encaminhe para sua aplicação local: -abacatepay login -l +```bash +abacatepay listen --forward-to http://localhost:3000/webhooks/abacatepay ``` -

Autenticação

+Flags úteis: -

A CLI usa OAuth2 Device Flow, sem necessidade de copiar tokens manualmente, apenas seguindo o fluxo abaixo

+| Flag | Descrição | Padrão | +| ---- | --------- | ------ | +| `--forward-to` | URL local que receberá os eventos | `http://localhost:3000/webhooks/abacatepay` | +| `--mock` | Gera webhooks locais sem conectar na API | `false` | -1. Use `abacatepay login` -2. Abra a URL exibida no navegador -3. Autorize o acesso na sua conta AbacatePay -4. A CLI detecta a autorização automaticamente -5. Informe a URL para encaminhar webhooks (Ou pressione a tecla Enter para usar o padrão) +A CLI assina os eventos encaminhados com o header `X-Abacate-Signature`, facilitando testes locais de validação de assinatura. -

Armazenamento

+## Pagamentos em dev mode -

O token da sua conta é armazenado com segurança no keyring nativo do seu sistema operacional:

- -- **macOS**: Keychain -- **Linux**: gnome-keyring ou kwallet -- **Windows**: Credential Manager - -

Caso o token não consiga ser salvo, você deverá instalar o keyring no seu sistema operacional (Linux)

+Simule o pagamento de uma cobrança transparente criada com uma chave de dev mode: ```bash -# Debian/Ubuntu -sudo apt install gnome-keyring - -# Fedora -sudo dnf install gnome-keyring +abacatepay payments simulate ``` -

Logs

+Esse comando chama a API v2: + +```http +POST /v2/transparents/simulate-payment?id= +``` -

Todos os logs são salvos ni caminho ~/.abacatepay/logs/ com uma rotação automática de 10mb por arquivo, 5 backups e 30 dias de retenção

+Ele só funciona para cobranças em `devMode`. Em produção, o pagamento precisa acontecer pelo fluxo real. -- **abacatepay.log** - Log geral (JSON) -- **transactions.log** - Webhooks recebidos e encaminhados +## Output -

Use jq para analisar os logs

+A flag global `--output` permite trocar o formato de saída: ```bash -# Erros -cat ~/.abacatepay/logs/abacatepay.log | jq 'select(.level=="ERROR")' +abacatepay payments simulate --output json +``` -# Webhooks recebidos -cat ~/.abacatepay/logs/transactions.log | jq 'select(.msg=="webhook_received")' +Formatos disponíveis: `text`, `json` e `table`. -# Tempo médio de encaminhamento -cat ~/.abacatepay/logs/transactions.log | jq 'select(.msg=="webhook_forwarded") | .duration_ms' | jq -s 'add/length' -``` +## Escopo atual + +Para manter a CLI simples e alinhada com a API v2, o escopo atual é: -

Documentação

+- login/logout; +- receber webhooks; +- simular pagamento de cobranças transparentes em dev mode. -

Para uma documentação completa, veja a documentação oficial.

+Recursos fora desse escopo foram removidos da codebase até existir paridade clara com a API v2 e documentação atualizada. diff --git a/cmd/abacatepay/main.go b/cmd/abacatepay/main.go new file mode 100644 index 0000000..88b951e --- /dev/null +++ b/cmd/abacatepay/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "os" + + rootcmd "github.com/AbacatePay/abacatepay-cli/cmd" + "github.com/AbacatePay/abacatepay-cli/internal/logger" +) + +func main() { + logCfg, err := logger.DefaultConfig() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to configure logger: %v\n", err) + os.Exit(1) + } + + if _, err := logger.Setup(logCfg); err != nil { + fmt.Fprintf(os.Stderr, "Failed to initialize logger: %v\n", err) + os.Exit(1) + } + + rootcmd.Exec() +} diff --git a/cmd/docs.go b/cmd/docs.go deleted file mode 100644 index 35f2435..0000000 --- a/cmd/docs.go +++ /dev/null @@ -1,20 +0,0 @@ -package cmd - -import ( - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var docsCmd = &cobra.Command{ - Use: "docs", - Aliases: []string{"documentation"}, - Short: "Open the CLI documentation in your browser", - RunE: func(cmd *cobra.Command, args []string) error { - return utils.OpenBrowser("https://docs.abacatepay.com/pages/cli") - }, -} - -func init() { - rootCmd.AddCommand(docsCmd) -} diff --git a/cmd/events.go b/cmd/events.go deleted file mode 100644 index 93e1124..0000000 --- a/cmd/events.go +++ /dev/null @@ -1,13 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -var eventsCmd = &cobra.Command{ - Use: "events", -} - -func init() { - rootCmd.AddCommand(eventsCmd) -} diff --git a/cmd/events_resend.go b/cmd/events_resend.go deleted file mode 100644 index b621df1..0000000 --- a/cmd/events_resend.go +++ /dev/null @@ -1,91 +0,0 @@ -package cmd - -import ( - "fmt" - "net/http" - "time" - - "abacatepay-cli/internal/crypto" - "abacatepay-cli/internal/logger" - "abacatepay-cli/internal/style" - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var resendForwardURL string - -var eventsResendCmd = &cobra.Command{ - Use: "resend ", - Short: "Resend a past event to your local webhook endpoint", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return resendEvent(args[0]) - }, -} - -func init() { - eventsResendCmd.Flags().StringVar(&resendForwardURL, "forward-to", "", "URL to forward the event to") - eventsCmd.AddCommand(eventsResendCmd) -} - -func resendEvent(id string) error { - entry, err := logger.FindLogEntryByID(id) - if err != nil { - return err - } - - if entry.RawMessage == "" { - return fmt.Errorf("event %s found but has no raw payload stored", id) - } - - 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) - - secret := "whsec_abacate_local_dev_secret" - timestamp := time.Now().Unix() - signature := crypto.SignWebhookPayload(secret, timestamp, []byte(entry.RawMessage)) - - style.LogSigningSecret(secret) - 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(url) - - duration := time.Since(startTime) - - if err != nil { - style.PrintError(fmt.Sprintf("Failed to forward: %v", err)) - return nil - } - - style.LogWebhookForwarded(resp.StatusCode(), http.StatusText(resp.StatusCode()), entry.Event) - - if resp.StatusCode() >= 200 && resp.StatusCode() < 300 { - style.PrintSuccess("Event resent successfully", map[string]string{ - "ID": id, - "Status": fmt.Sprintf("%d %s", resp.StatusCode(), http.StatusText(resp.StatusCode())), - "Duration": fmt.Sprintf("%dms", duration.Milliseconds()), - }) - - return nil - } - - fmt.Printf("\nServer responded with error status: %d\n", resp.StatusCode()) - fmt.Println(string(resp.Body())) - - return nil -} diff --git a/cmd/events_sample.go b/cmd/events_sample.go deleted file mode 100644 index 9d0ad59..0000000 --- a/cmd/events_sample.go +++ /dev/null @@ -1,44 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/mock" - "abacatepay-cli/internal/style" - - "github.com/spf13/cobra" -) - -var eventsSampleCmd = &cobra.Command{ - Use: "sample ", - Short: "Generate a sample JSON payload for a specific event", - Example: "abacatepay events billing.paid\n abacatepay events payout.done", - Args: cobra.ExactArgs(1), - ValidArgs: []string{"billing.paid", "payout.done", "payout.failed"}, - RunE: func(cmd *cobra.Command, args []string) error { - return events(args[0]) - }, -} - -func init() { - eventsCmd.AddCommand(eventsSampleCmd) -} - -func events(evt string) error { - var data any - - switch evt { - case "billing.paid": - data = mock.MockBillingPaidEvent() - case "payout.done": - data = mock.MockPayoutEvent(true) - case "payout.failed": - data = mock.MockPayoutEvent(false) - default: - return fmt.Errorf("unknown event type: %s. Available: billing.paid, payout.done, payout.failed", evt) - } - - style.PrintJSON(data) - - return nil -} diff --git a/cmd/init.go b/cmd/init.go deleted file mode 100644 index 3fefd06..0000000 --- a/cmd/init.go +++ /dev/null @@ -1,121 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/scaffold" - "abacatepay-cli/internal/style" - - "github.com/spf13/cobra" -) - -type projectConfig struct { - Name string - Framework string - Linter string - BetterAuth bool -} - -var initCmd = &cobra.Command{ - Use: "init [project-name]", - Short: "Initialize a new AbacatePay project", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - var name string - if len(args) > 0 { - name = args[0] - } - return initializeProject(name) - }, -} - -func init() { - rootCmd.AddCommand(initCmd) -} - -func initializeProject(name string) error { - cfg := projectConfig{} - - if name != "" { - cfg.Name = name - } - - if cfg.Name == "" { - if err := style.Input("Project name", "my-app", &cfg.Name, scaffold.ValidateProjectName); err != nil { - return err - } - } - - if err := scaffold.ValidateProjectName(cfg.Name); err != nil { - return err - } - - frameworkOptions := map[string]string{ - "Next.js": "next", - "Elysia": "elysia", - } - framework, err := style.Select("Which framework do you want to use?", frameworkOptions) - if err != nil { - return err - } - cfg.Framework = framework - - linterOptions := map[string]string{ - "ESLint": "eslint", - "Biome": "biome", - } - linter, err := style.Select("Which linter do you want to use?", linterOptions) - if err != nil { - return err - } - cfg.Linter = linter - - if err := style.Confirm("Do you want BetterAuth already configured?", &cfg.BetterAuth); err != nil { - return err - } - - betterAuthLabel := "No" - if cfg.BetterAuth { - betterAuthLabel = "Yes" - } - - frameworkLabel := "Next.js" - if cfg.Framework == "elysia" { - frameworkLabel = "Elysia" - } - - linterLabel := "ESLint" - if cfg.Linter == "biome" { - linterLabel = "Biome" - } - - style.PrintSuccess("Project configured!", map[string]string{ - "Name": cfg.Name, - "Framework": frameworkLabel, - "Linter": linterLabel, - "BetterAuth": betterAuthLabel, - }) - - fmt.Printf("\n🥑 Creating project %s...\n\n", cfg.Name) - - scaffoldCfg := scaffold.Config{ - ProjectName: cfg.Name, - Framework: cfg.Framework, - Linter: cfg.Linter, - BetterAuth: cfg.BetterAuth, - } - - if err := scaffold.ScaffoldProject(scaffoldCfg, "."); err != nil { - return fmt.Errorf("failed to scaffold project: %w", err) - } - - fmt.Println("✅ Project created successfully!") - fmt.Println("Next steps:") - - steps := scaffoldCfg.GetNextSteps() - for _, step := range steps { - fmt.Printf(" %s\n", step) - } - - return nil -} diff --git a/cmd/listen.go b/cmd/listen.go index 7405cf4..0431c61 100644 --- a/cmd/listen.go +++ b/cmd/listen.go @@ -6,7 +6,7 @@ import ( "os/signal" "syscall" - "abacatepay-cli/internal/utils" + "github.com/AbacatePay/abacatepay-cli/internal/utils" "github.com/spf13/cobra" ) diff --git a/cmd/login.go b/cmd/login.go index 0c8f301..c4af643 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -7,8 +7,8 @@ import ( "os/signal" "syscall" - "abacatepay-cli/internal/auth" - "abacatepay-cli/internal/utils" + "github.com/AbacatePay/abacatepay-cli/internal/auth" + "github.com/AbacatePay/abacatepay-cli/internal/utils" "github.com/spf13/cobra" ) diff --git a/cmd/logout.go b/cmd/logout.go index b5fe83d..6138d63 100644 --- a/cmd/logout.go +++ b/cmd/logout.go @@ -1,9 +1,9 @@ package cmd import ( - "abacatepay-cli/internal/auth" - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/utils" + "github.com/AbacatePay/abacatepay-cli/internal/auth" + "github.com/AbacatePay/abacatepay-cli/internal/output" + "github.com/AbacatePay/abacatepay-cli/internal/utils" "github.com/spf13/cobra" ) diff --git a/cmd/logs.go b/cmd/logs.go deleted file mode 100644 index dda6e60..0000000 --- a/cmd/logs.go +++ /dev/null @@ -1,14 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -var logsCmd = &cobra.Command{ - Use: "logs", - Short: "Display recent webhook transaction logs", -} - -func init() { - rootCmd.AddCommand(logsCmd) -} diff --git a/cmd/logs_list.go b/cmd/logs_list.go deleted file mode 100644 index 13c2b24..0000000 --- a/cmd/logs_list.go +++ /dev/null @@ -1,94 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/logger" - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/style" - - "github.com/spf13/cobra" -) - -var ( - logsLimit int - logsTypeFilter string -) - -var logsListCmd = &cobra.Command{ - Use: "list", - Short: "List historical webhook events from local log file", - Long: "Display webhook transactions recorded locally during listen sessions", - RunE: func(cmd *cobra.Command, args []string) error { - return listLogs() - }, -} - -func init() { - logsListCmd.Flags().IntVarP(&logsLimit, "limit", "n", 50, "Number of log entries to display") - logsListCmd.Flags().StringVarP(&logsTypeFilter, "type", "t", "", "Filter by log type (webhook_received, webhook_forwarded, webhook_forward_failed, webhook_forward_error)") - - logsCmd.AddCommand(logsListCmd) -} - -func listLogs() error { - opts := logger.ReadOptions{ - Limit: logsLimit, - TypeFilter: logsTypeFilter, - } - - entries, err := logger.ReadTransactionLogs(opts) - if err != nil { - return err - } - - if len(entries) == 0 { - logPath, _ := logger.GetLogFilePath() - fmt.Printf("No transaction logs found.\n") - fmt.Printf("Hint: Run 'abacatepay listen' to start recording webhook events.\n") - fmt.Printf("Log file location: %s\n", logPath) - return nil - } - - if output.GetFormat() == output.FormatJSON { - return printLogsJSON(entries) - } - return printLogsTable(entries) -} - -func printLogsJSON(entries []logger.LogEntry) error { - style.PrintJSON(map[string]any{ - "logs": entries, - "count": len(entries), - }) - return nil -} - -func printLogsTable(entries []logger.LogEntry) error { - var rows [][]string - - for i := len(entries) - 1; i >= 0; i-- { - entry := entries[i] - - status := entry.Msg - if entry.StatusCode > 0 { - status = fmt.Sprintf("%s [%d]", entry.Msg, entry.StatusCode) - } - - timestamp := entry.Time - if entry.Timestamp != "" { - timestamp = entry.Timestamp - } - - rows = append(rows, []string{ - timestamp, - status, - entry.ID, - entry.URL, - }) - } - - style.PrintTable([]string{"Timestamp", "Type", "ID", "URL"}, rows) - - return nil -} diff --git a/cmd/logs_tail.go b/cmd/logs_tail.go deleted file mode 100644 index 953553f..0000000 --- a/cmd/logs_tail.go +++ /dev/null @@ -1,56 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "os" - "os/signal" - "syscall" - - "abacatepay-cli/internal/utils" - "abacatepay-cli/internal/webhook" - - "github.com/spf13/cobra" -) - -var logsTailCmd = &cobra.Command{ - Use: "tail", - Short: "Stream live webhook events in real-time", - Long: "Connect to the WebSocket and display incoming webhook events as they arrive", - RunE: func(cmd *cobra.Command, args []string) error { - return logsTail() - }, -} - -func init() { - logsCmd.AddCommand(logsTailCmd) -} - -func logsTail() error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - return err - } - - ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - defer cancel() - - listener := webhook.NewTailListener(deps.Config, deps.Config.TokenKey) - - fmt.Println("Streaming webhook events...") - fmt.Println("\nPress Ctrl+C to stop") - - go func() { - <-ctx.Done() - fmt.Println("\nListener stopped") - }() - - err = listener.Listen(ctx) - if err == nil { - return nil - } - if ctx.Err() != nil { - return nil - } - return fmt.Errorf("couldn't start the tail listener: %w", err) -} diff --git a/cmd/payments.go b/cmd/payments.go index 1012ba4..1919263 100644 --- a/cmd/payments.go +++ b/cmd/payments.go @@ -3,7 +3,8 @@ package cmd import "github.com/spf13/cobra" var paymentsCmd = &cobra.Command{ - Use: "payments", + Use: "payments", + Short: "Payment utilities for AbacatePay dev mode", } func init() { diff --git a/cmd/payments_check.go b/cmd/payments_check.go deleted file mode 100644 index dbee747..0000000 --- a/cmd/payments_check.go +++ /dev/null @@ -1,26 +0,0 @@ -package cmd - -import ( - "abacatepay-cli/internal/payments" - - "github.com/spf13/cobra" -) - -var checkPaymentCmd = &cobra.Command{ - Use: "check", - Short: "", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return check(args[0]) - }, -} - -func init() { - paymentsCmd.AddCommand(checkPaymentCmd) -} - -func check(paymentID string) error { - 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 deleted file mode 100644 index aa56125..0000000 --- a/cmd/payments_create.go +++ /dev/null @@ -1,96 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/mock" - "abacatepay-cli/internal/payments" - "abacatepay-cli/internal/prompts" - "abacatepay-cli/internal/style" - "abacatepay-cli/internal/types" - "abacatepay-cli/internal/utils" - - v1 "github.com/almeidazs/go-abacate-types/v1" - "github.com/spf13/cobra" -) - -var createInteractive bool - -var createPaymentCmd = &cobra.Command{ - Use: "create [pix|checkout]", - Short: "Create a new payment charge", - Long: `Create a new payment charge. -By default, creates a mock payment with random data. -Use -i to enter interactive mode and specify details.`, - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - var method string - if len(args) > 0 { - method = args[0] - } - - return createPayment(method) - }, -} - -func init() { - paymentsCmd.AddCommand(createPaymentCmd) - - createPaymentCmd.Flags().BoolVarP(&createInteractive, "interactive", "i", false, "Enable interactive mode") -} - -func createPayment(method string) error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - return err - } - - if method == "" { - options := map[string]string{ - "PIX QR Code": "pix", - "Checkout": "checkout", - } - - method, err = style.Select("🥑 Choose payment method\n", options) - if err != nil { - return err - } - } - - service := payments.New(deps.Client, deps.Config.APIBaseURL, Verbose) - - switch method { - case "pix", "pix_qrcode": - if !createInteractive { - body := mock.CreatePixQRCodeMock() - _, err := service.CreatePixQRCode(body, false) - return err - } - - body := &v1.RESTPostCreateQRCodePixBody{ - Customer: &v1.APICustomerMetadata{}, - } - if err := prompts.PromptForPIXQRCodeData(body); err != nil { - return fmt.Errorf("failed to prompt pix qrcode data: %w", err) - } - _, err := service.CreatePixQRCode(body, false) - return err - - case "checkout": - if !createInteractive { - body := mock.CreateCheckoutMock() - return service.CreateCheckout(body) - } - - body := &types.CreateCheckoutRequest{ - Customer: &types.Customer{}, - } - if err := prompts.PromptForCheckout(body); err != nil { - return fmt.Errorf("failed to prompt checkout data: %w", err) - } - return service.CreateCheckout(body) - - default: - return fmt.Errorf("invalid payment method: %s. Use 'pix' or 'checkout'", method) - } -} diff --git a/cmd/payments_simulate.go b/cmd/payments_simulate.go index 1be7ac6..1f311ed 100644 --- a/cmd/payments_simulate.go +++ b/cmd/payments_simulate.go @@ -1,15 +1,18 @@ package cmd import ( - "abacatepay-cli/internal/payments" + "github.com/AbacatePay/abacatepay-cli/internal/payments" "github.com/spf13/cobra" ) var simulatePaymentCmd = &cobra.Command{ - Use: "simulate", - Args: cobra.ExactArgs(1), - Short: "", + Use: "simulate ", + Aliases: []string{"simulate-payment"}, + Args: cobra.ExactArgs(1), + Short: "Simulate payment for a transparent checkout in dev mode", + Long: "Simulates payment for a transparent checkout created with a dev-mode API key. " + + "This calls the API v2 endpoint POST /v2/transparents/simulate-payment?id=.", RunE: func(cmd *cobra.Command, args []string) error { return simulate(args[0]) }, @@ -21,6 +24,6 @@ func init() { func simulate(paymentID string) error { return payments.ExecutePaymentAction(Local, Verbose, func(s *payments.Service) error { - return s.SimulatePixQRCodePayment(paymentID, false) + return s.SimulateTransparentPayment(paymentID) }) } diff --git a/cmd/profile.go b/cmd/profile.go deleted file mode 100644 index c12b3e8..0000000 --- a/cmd/profile.go +++ /dev/null @@ -1,16 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" -) - -var profileCmd = &cobra.Command{ - Use: "profile", - Aliases: []string{"profiles"}, - Short: "Manage saved authentication profiles", - Long: "Allows listing, switching, and removing AbacatePay access profiles configured on your machine.", -} - -func init() { - rootCmd.AddCommand(profileCmd) -} diff --git a/cmd/profile_delete.go b/cmd/profile_delete.go deleted file mode 100644 index 409f883..0000000 --- a/cmd/profile_delete.go +++ /dev/null @@ -1,50 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var deleteProfileCmd = &cobra.Command{ - Use: "delete [name]", - Short: "Remove a profile", - Args: cobra.ExactArgs(1), - Aliases: []string{"remove", "rm"}, - RunE: func(cmd *cobra.Command, args []string) error { - return deleteProfile(args[0]) - }, -} - -func init() { - profileCmd.AddCommand(deleteProfileCmd) -} - -func deleteProfile(name string) error { - deps := utils.SetupDependencies(Local, Verbose) - - token, err := deps.Store.GetNamed(name) - if err != nil { - return fmt.Errorf("error verifying profile: %w", err) - } - - if token == "" { - return fmt.Errorf("profile '%s' not found", name) - } - - active, _ := deps.Store.GetActiveProfile() - - if active == name { - return fmt.Errorf("cannot delete the active profile. Switch to another profile first using 'profile use'") - } - - if err := deps.Store.DeleteNamed(name); err != nil { - return fmt.Errorf("error deleting profile: %w", err) - } - - fmt.Printf("Profile '%s' successfully removed.\n", name) - - return nil -} diff --git a/cmd/profile_list.go b/cmd/profile_list.go deleted file mode 100644 index 9a17716..0000000 --- a/cmd/profile_list.go +++ /dev/null @@ -1,63 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var listProfilesCmd = &cobra.Command{ - Use: "list", - Short: "List all configured profiles", - RunE: func(cmd *cobra.Command, args []string) error { - return listProfiles() - }, -} - -func init() { - profileCmd.AddCommand(listProfilesCmd) -} - -func listProfiles() error { - deps := utils.SetupDependencies(Local, Verbose) - - profiles, err := deps.Store.List() - if err != nil { - return fmt.Errorf("error listing profiles: %w", err) - } - - active, err := deps.Store.GetActiveProfile() - if err != nil { - active = "" - } - - if len(profiles) == 0 { - output.Print(output.Result{ - Title: "No profiles found", - Fields: map[string]string{ - "Hint": "Use 'abacatepay login' to create one.", - }, - Data: map[string]any{ - "profiles": []string{}, - "active": "", - }, - }) - return nil - } - - outputProfiles := make([]output.Profile, 0, len(profiles)) - for _, name := range profiles { - token, _ := deps.Store.GetNamed(name) - outputProfiles = append(outputProfiles, output.Profile{ - Name: name, - Token: token, - Active: name == active, - }) - } - - output.PrintProfiles(outputProfiles, active) - return nil -} diff --git a/cmd/root.go b/cmd/root.go index 5a16c13..be9adda 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,9 +5,9 @@ import ( "log/slog" "os" - "abacatepay-cli/internal/logger" - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/version" + "github.com/AbacatePay/abacatepay-cli/internal/logger" + "github.com/AbacatePay/abacatepay-cli/internal/output" + "github.com/AbacatePay/abacatepay-cli/internal/version" "github.com/spf13/cobra" ) @@ -18,6 +18,9 @@ var rootCmd = &cobra.Command{ Version: version.Version, SilenceUsage: true, SilenceErrors: true, + CompletionOptions: cobra.CompletionOptions{ + DisableDefaultCmd: true, + }, } var ( @@ -27,7 +30,7 @@ var ( func Exec() { rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "Enable verbose logging") - rootCmd.PersistentFlags().BoolVarP(&Local, "local", "l", false, "Use test server") + rootCmd.PersistentFlags().BoolVarP(&Local, "local", "l", false, "Deprecated: API environment is determined by the API key") rootCmd.PersistentFlags().StringVarP(&OutputFormat, "output", "o", "text", "Output format: text, json, table") rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { diff --git a/cmd/status.go b/cmd/status.go deleted file mode 100644 index 8d1d791..0000000 --- a/cmd/status.go +++ /dev/null @@ -1,49 +0,0 @@ -package cmd - -import ( - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var statusCmd = &cobra.Command{ - Use: "status", - Aliases: []string{"doctor"}, - Short: "Check authentication status", - RunE: func(cmd *cobra.Command, args []string) error { - return getAuthStatus() - }, -} - -func init() { - rootCmd.AddCommand(statusCmd) -} - -func getAuthStatus() error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - output.Error("You are not authenticated. Use 'abacatepay login' to start.") - return nil - } - - activeProfile, err := deps.Store.GetActiveProfile() - if err != nil || activeProfile == "" { - output.Error("No active profile found.") - return nil - } - - output.Print(output.Result{ - Title: "Connected successfully", - Fields: map[string]string{ - "Profile": activeProfile, - "Status": "Online", - }, - Data: map[string]string{ - "profile": activeProfile, - "status": "online", - }, - }) - - return nil -} diff --git a/cmd/switch.go b/cmd/switch.go deleted file mode 100644 index 37877ec..0000000 --- a/cmd/switch.go +++ /dev/null @@ -1,46 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var switchCmd = &cobra.Command{ - Use: "switch", - Aliases: []string{"use"}, - Args: cobra.ExactArgs(1), - Short: "Switch to another existing profile", - RunE: func(cmd *cobra.Command, args []string) error { - return switchProfile(args[0]) - }, -} - -func init() { - rootCmd.AddCommand(switchCmd) -} - -func switchProfile(name string) error { - if !utils.IsOnline() { - return fmt.Errorf("you’re offline — check your connection and try again") - } - - deps := utils.SetupDependencies(Local, Verbose) - - token, err := deps.Store.GetNamed(name) - if err != nil { - return fmt.Errorf("error searching for profile: %w", err) - } - if token == "" { - return fmt.Errorf("profile '%s' not found", name) - } - - if err := deps.Store.SetActiveProfile(name); err != nil { - return fmt.Errorf("error setting active profile: %w", err) - } - - fmt.Printf("Now using profile: %s\n", name) - return nil -} diff --git a/cmd/trigger.go b/cmd/trigger.go deleted file mode 100644 index 28e38d8..0000000 --- a/cmd/trigger.go +++ /dev/null @@ -1,89 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/mock" - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/payments" - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var triggerCmd = &cobra.Command{ - Use: "trigger ", - Args: cobra.ExactArgs(1), - Short: "Trigger test events", - RunE: func(cmd *cobra.Command, args []string) error { - return trigger(args[0]) - }, -} - -func init() { - rootCmd.AddCommand(triggerCmd) -} - -func trigger(evt string) error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - return err - } - - return handleEvent(deps, evt) -} - -func handleEvent(deps *utils.Dependencies, evt string) error { - service := payments.New(deps.Client, deps.Config.APIBaseURL, Verbose) - - switch evt { - case "billing.paid": - body := mock.CreatePixQRCodeMock() - - pixID, err := service.CreatePixQRCode(body, true) - if err != nil { - return err - } - - if err := service.SimulatePixQRCodePayment(pixID, true); err != nil { - return err - } - - output.Print(output.Result{ - Title: "Billing.paid Triggered", - Fields: map[string]string{ - "Charge ID": pixID, - "Status": "Simulated", - "Note": "Check your 'listen' terminal for the webhook event", - }, - Data: map[string]any{ - "event": evt, - "chargeId": pixID, - }, - }) - - return nil - - case "payout.done", "payout.failed": - isDone := evt == "payout.done" - mockEvent := mock.MockPayoutEvent(isDone) - - output.Print(output.Result{ - Title: fmt.Sprintf("Mock %s Triggered", evt), - Fields: map[string]string{ - "Event ID": mockEvent.ID, - "Transaction ID": mockEvent.Data.Transaction.ID, - "Status": mockEvent.Data.Transaction.Status, - }, - Data: mockEvent, - }) - - fmt.Printf("\nTip: Use 'abacatepay events resend %s' to send this mock to your local server.\n", mockEvent.ID) - - // NOTE: We could automatically append this mock to the local log file, so it appears in 'logs list' immediately. - return nil - - default: - return fmt.Errorf("invalid event type: %s. Supported: billing.paid, payout.done, payout.failed", evt) - } -} diff --git a/cmd/update.go b/cmd/update.go deleted file mode 100644 index 689a993..0000000 --- a/cmd/update.go +++ /dev/null @@ -1,52 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "os" - - "abacatepay-cli/internal/version" - - "github.com/creativeprojects/go-selfupdate" - "github.com/spf13/cobra" -) - -var updateCmd = &cobra.Command{ - Use: "update", - Aliases: []string{"upgrade"}, - Short: "Update CLI to new version if it is available", - RunE: func(cmd *cobra.Command, args []string) error { - return update() - }, -} - -func init() { - rootCmd.AddCommand(updateCmd) -} - -func update() error { - ctx := context.Background() - - latest, found, err := version.CheckUpdate(ctx, rootCmd.Version) - if err != nil { - return fmt.Errorf("couldn’t check for updates: %w", err) - } - - if !found { - fmt.Printf("You’re already on the latest version (%s)\n", rootCmd.Version) - return nil - } - - fmt.Printf("Update available: %s\n", latest.Version()) - fmt.Println("Downloading and installing...") - - exe, _ := os.Executable() - - if err := selfupdate.UpdateTo(ctx, latest.AssetURL, latest.AssetName, exe); err != nil { - return fmt.Errorf("update failed: %w", err) - } - - fmt.Println("Update complete ✨") - - return nil -} diff --git a/cmd/verify.go b/cmd/verify.go deleted file mode 100644 index a71d94b..0000000 --- a/cmd/verify.go +++ /dev/null @@ -1,138 +0,0 @@ -package cmd - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "fmt" - "strconv" - "strings" - "time" - - "abacatepay-cli/internal/style" - - "github.com/spf13/cobra" -) - -var ( - verifySecret string - verifySignature string - verifyPayload string -) - -var verifyCmd = &cobra.Command{ - Use: "verify", - Short: "Verify a webhook signature locally", - Long: `Debug and verify webhook signatures offline. - -This command calculates the expected signature for a given payload and secret, -then compares it with the signature header provided. - -Example: - abacatepay verify \ - --secret "whsec_..." \ - --payload '{"id":"evt_..."}' \ - --signature "t=123456,v1=abcdef..."`, - RunE: func(cmd *cobra.Command, args []string) error { - return verify() - }, -} - -func init() { - verifyCmd.Flags().StringVar(&verifySecret, "secret", "", "Webhook signing secret (starts with whsec_)") - verifyCmd.Flags().StringVar(&verifyPayload, "payload", "", "Raw JSON payload body") - verifyCmd.Flags().StringVar(&verifySignature, "signature", "", "The value of X-Abacate-Signature header") - - _ = verifyCmd.MarkFlagRequired("secret") - _ = verifyCmd.MarkFlagRequired("payload") - _ = verifyCmd.MarkFlagRequired("signature") - - rootCmd.AddCommand(verifyCmd) -} - -type signatureParts struct { - timestamp int64 - signature string -} - -func verify() error { - parts, err := parseSignatureHeader(verifySignature) - if err != nil { - style.PrintError(err.Error()) - return err - } - - expectedSig := computeSignature(verifySecret, parts.timestamp, verifyPayload) - isValid := hmac.Equal([]byte(parts.signature), []byte(expectedSig)) - - if !isValid { - style.PrintVerifyError(expectedSig, parts.signature) - return fmt.Errorf("signature mismatch") - } - - printVerifySuccess(parts.timestamp, verifySecret) - return nil -} - -func parseSignatureHeader(header string) (*signatureParts, error) { - var timestampStr, signature string - - for part := range strings.SplitSeq(header, ",") { - kv := strings.SplitN(strings.TrimSpace(part), "=", 2) - if len(kv) != 2 { - continue - } - - switch kv[0] { - case "t": - timestampStr = kv[1] - case "v1": - signature = kv[1] - } - } - - if timestampStr == "" || signature == "" { - return nil, fmt.Errorf("invalid signature format. Expected: t=TIMESTAMP,v1=SIGNATURE") - } - - ts, err := strconv.ParseInt(timestampStr, 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid timestamp in signature header") - } - - return &signatureParts{ - timestamp: ts, - signature: signature, - }, nil -} - -func computeSignature(secret string, timestamp int64, payload string) string { - signedPayload := fmt.Sprintf("%d.%s", timestamp, payload) - mac := hmac.New(sha256.New, []byte(secret)) - mac.Write([]byte(signedPayload)) - return hex.EncodeToString(mac.Sum(nil)) -} - -func printVerifySuccess(timestamp int64, secret string) { - fields := map[string]string{ - "Timestamp": formatTimestamp(timestamp), - "Secret": maskSecret(secret), - "Status": "VALID", - } - style.PrintSuccess("Signature Verified", fields) -} - -func formatTimestamp(ts int64) string { - diff := time.Since(time.Unix(ts, 0)) - if diff > 5*time.Minute { - return fmt.Sprintf("%d (Warning: %s old)", ts, diff.Round(time.Second)) - } - return fmt.Sprintf("%d", ts) -} - -func maskSecret(s string) string { - if len(s) < 8 { - return s - } - return s[:6] + "..." + s[len(s)-4:] -} diff --git a/cmd/whoami.go b/cmd/whoami.go deleted file mode 100644 index 5bee0d0..0000000 --- a/cmd/whoami.go +++ /dev/null @@ -1,56 +0,0 @@ -package cmd - -import ( - "fmt" - - "abacatepay-cli/internal/auth" - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/utils" - - "github.com/spf13/cobra" -) - -var whoamiCmd = &cobra.Command{ - Use: "whoami", - Short: "Display current profile and authentication status", - RunE: func(cmd *cobra.Command, args []string) error { - return whoami() - }, -} - -func init() { - rootCmd.AddCommand(whoamiCmd) -} - -func whoami() error { - deps, err := utils.SetupClient(Local, Verbose) - if err != nil { - return err - } - - activeProfile, _ := deps.Store.GetActiveProfile() - token, _ := deps.Store.GetNamed(activeProfile) - - user, err := auth.ValidateToken(deps.Client, deps.Config.APIBaseURL, token) - if err != nil { - return fmt.Errorf("session expired: %w", err) - } - - output.Print(output.Result{ - Title: "User Information", - Fields: map[string]string{ - "Profile": activeProfile, - "User": user.Name, - "Email": user.Email, - "Status": "Authenticated", - }, - Data: map[string]any{ - "profile": activeProfile, - "user": user.Name, - "email": user.Email, - "status": "authenticated", - }, - }) - - return nil -} diff --git a/go.mod b/go.mod index d28f914..3893c17 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,12 @@ -module abacatepay-cli +module github.com/AbacatePay/abacatepay-cli go 1.25.5 require ( github.com/99designs/keyring v1.2.2 - github.com/almeidazs/go-abacate-types v1.0.0 github.com/briandowns/spinner v1.23.0 - github.com/brianvoe/gofakeit/v7 v7.14.0 github.com/charmbracelet/huh v0.8.0 github.com/charmbracelet/lipgloss v1.1.0 - github.com/creativeprojects/go-selfupdate v1.5.2 github.com/go-resty/resty/v2 v2.11.0 github.com/gorilla/websocket v1.5.3 github.com/spf13/cobra v1.8.1 @@ -18,10 +15,7 @@ require ( ) require ( - code.gitea.io/sdk/gitea v0.22.1 // indirect - github.com/42wim/httpsig v1.2.3 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.3.0 // indirect @@ -33,19 +27,12 @@ require ( github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/danieljoos/wincred v1.2.3 // indirect - github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.7.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fatih/color v1.16.0 // indirect - github.com/go-fed/httpsig v1.1.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect - github.com/google/go-github/v74 v74.0.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.8 // indirect - github.com/hashicorp/go-version v1.8.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -59,15 +46,11 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/ulikunitz/xz v0.5.15 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - gitlab.com/gitlab-org/api/client-go v1.9.1 // indirect - golang.org/x/crypto v0.46.0 // indirect + golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/term v0.38.0 // indirect golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.14.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c3f7818..6842595 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,9 @@ -code.gitea.io/sdk/gitea v0.22.1 h1:7K05KjRORyTcTYULQ/AwvlVS6pawLcWyXZcTr7gHFyA= -code.gitea.io/sdk/gitea v0.22.1/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM= -github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= -github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/almeidazs/go-abacate-types v1.0.0 h1:HjgGu04Ko+W8XKLBwcHTQAlIjvwOlZJbooPp2zhNXrU= -github.com/almeidazs/go-abacate-types v1.0.0/go.mod h1:WpwbrPLf32FcEAKI6OFAGUbKZjxk6zNDpQVuKMdj9HA= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -20,8 +12,6 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE= -github.com/brianvoe/gofakeit/v7 v7.14.0 h1:R8tmT/rTDJmD2ngpqBL9rAKydiL7Qr2u3CXPqRt59pk= -github.com/brianvoe/gofakeit/v7 v7.14.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws= @@ -55,14 +45,10 @@ github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJn github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/creativeprojects/go-selfupdate v1.5.2 h1:3KR3JLrq70oplb9yZzbmJ89qRP78D1AN/9u+l3k0LJ4= -github.com/creativeprojects/go-selfupdate v1.5.2/go.mod h1:BCOuwIl1dRRCmPNRPH0amULeZqayhKyY2mH/h4va7Dk= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= -github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvsekhvalnov/jose2go v1.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo= @@ -71,31 +57,14 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= -github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github/v74 v74.0.0 h1:yZcddTUn8DPbj11GxnMrNiAnXH14gNs559AsUpNpPgM= -github.com/google/go-github/v74 v74.0.0/go.mod h1:ubn/YdyftV80VPSI26nSJvaEsTOnsjrxG3o9kJhcyak= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= -github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= -github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= -github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -137,25 +106,16 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= -github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/gitlab-org/api/client-go v1.9.1 h1:tZm+URa36sVy8UCEHQyGGJ8COngV4YqMHpM6k9O5tK8= -gitlab.com/gitlab-org/api/client-go v1.9.1/go.mod h1:71yTJk1lnHCWcZLvM5kPAXzeJ2fn5GjaoV8gTOPd4ME= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -164,15 +124,12 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -207,7 +164,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/auth/api.go b/internal/auth/api.go index 116f3bb..b139754 100644 --- a/internal/auth/api.go +++ b/internal/auth/api.go @@ -1,13 +1,44 @@ -// Package auth... +// Package auth validates AbacatePay credentials. package auth import ( - "abacatepay-cli/internal/types" + "fmt" + "net/http" + + "github.com/AbacatePay/abacatepay-cli/internal/types" "github.com/go-resty/resty/v2" ) -// TODO: I`ll made this func when i get the right endpoint later func ValidateToken(client *resty.Client, baseURL, token string) (*types.User, error) { - return &types.User{Name: "Mock User", Email: "mock@example.com"}, nil + var result types.StoreResponse + + resp, err := client.R(). + SetAuthToken(token). + SetResult(&result). + Get(baseURL + "/v2/store/get") + if err != nil { + return nil, fmt.Errorf("failed to validate API key: %w", err) + } + + if resp.StatusCode() == http.StatusUnauthorized { + return nil, fmt.Errorf("invalid API key") + } + + if resp.IsError() { + if result.Error != "" { + return nil, fmt.Errorf("failed to validate API key: %s", result.Error) + } + return nil, fmt.Errorf("failed to validate API key: %s", resp.Status()) + } + + name := result.Data.Name + if name == "" { + name = result.Data.ID + } + if name == "" { + name = "AbacatePay user" + } + + return &types.User{Name: name}, nil } diff --git a/internal/auth/login.go b/internal/auth/login.go index f2f678b..7787832 100644 --- a/internal/auth/login.go +++ b/internal/auth/login.go @@ -10,11 +10,11 @@ import ( "github.com/go-resty/resty/v2" - "abacatepay-cli/internal/config" - "abacatepay-cli/internal/store" - "abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/config" + "github.com/AbacatePay/abacatepay-cli/internal/store" + "github.com/AbacatePay/abacatepay-cli/internal/style" - "abacatepay-cli/internal/types" + "github.com/AbacatePay/abacatepay-cli/internal/types" ) type LoginParams struct { @@ -32,11 +32,6 @@ func Login(params *LoginParams) error { return loginWithAPIKey(params) } - if params.Config.APIBaseURL == "http://191.252.202.128:8080" { - params.APIKey = "mock_token_local_dev" - return loginWithAPIKey(params) - } - return loginWithDeviceFlow(params) } @@ -50,7 +45,7 @@ func loginWithAPIKey(params *LoginParams) error { return err } - fmt.Printf("Welcome back, %s\nProfile: %s\n", user.Name, params.ProfileName) + fmt.Printf("Welcome back, %s\nProfile: %s\n", user.Name, profileName(params.ProfileName)) return nil } @@ -92,12 +87,16 @@ func loginWithDeviceFlow(params *LoginParams) error { return err } - fmt.Printf("Authenticated as %s\nProfile: %s\n", user.Name, params.ProfileName) + fmt.Printf("Authenticated as %s\nProfile: %s\n", user.Name, profileName(params.ProfileName)) return nil } func saveAndActivateProfile(st store.TokenStore, profile, token string) error { + if profile == "" { + profile = "default" + } + existingToken, _ := st.GetNamed(profile) if existingToken != "" { slog.Info("Updating existing profile", "name", profile) @@ -215,3 +214,10 @@ func pollForToken(ctx context.Context, cfg *config.Config, client *resty.Client, return "", fmt.Errorf("authorization timed out") } + +func profileName(name string) string { + if name == "" { + return "default" + } + return name +} diff --git a/internal/client/client.go b/internal/client/client.go index 399ce11..98c1dcb 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -3,11 +3,11 @@ package client import ( "github.com/go-resty/resty/v2" - "abacatepay-cli/internal/config" + "github.com/AbacatePay/abacatepay-cli/internal/config" ) func New(cfg *config.Config) *resty.Client { return resty.New(). SetTimeout(cfg.HTTPTimeout). - SetHeader("User-Agent", "abacatepay-cli/1.0") + SetHeader("User-Agent", "github.com/AbacatePay/abacatepay-cli/1.0") } diff --git a/internal/config/config.go b/internal/config/config.go index 35c361c..e194954 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,14 +19,14 @@ func Default() *Config { ServiceName: "abacatepay-cli", TokenKey: "auth-token", HTTPTimeout: 15 * time.Second, - DefaultForwardURL: "http://localhost:3000/webhooks", + DefaultForwardURL: "http://localhost:3000/webhooks/abacatepay", Verbose: false, } } func Local() *Config { - cfg := Default() - cfg.APIBaseURL = "http://191.252.202.128:8080" - cfg.WebSocketBaseURL = "ws://191.252.202.128:8080/ws" - return cfg + // Kept for backwards compatibility with the old --local flag. API v2 uses the + // same public endpoint for production and dev mode; the API key determines + // the environment. + return Default() } diff --git a/internal/mock/events.go b/internal/mock/events.go deleted file mode 100644 index 5457e94..0000000 --- a/internal/mock/events.go +++ /dev/null @@ -1,72 +0,0 @@ -package mock - -import ( - "fmt" - "time" - - "abacatepay-cli/internal/types" - - "github.com/brianvoe/gofakeit/v7" -) - -func MockBillingPaidEvent() *types.BillingPaidEvent { - amount := gofakeit.Number(100, 1000) - id := fmt.Sprintf("evt_%s", gofakeit.LetterN(10)) - - return &types.BillingPaidEvent{ - ID: id, - Data: struct { - Payment *types.EventPayment `json:"payment,omitempty"` - Billing *types.EventBilling `json:"billing,omitempty"` - }{ - Payment: &types.EventPayment{ - Amount: amount, - Fee: gofakeit.Number(10, 100), - Method: "PIX", - }, - Billing: &types.EventBilling{ - ID: fmt.Sprintf("bill_%s", gofakeit.LetterN(10)), - ExternalID: gofakeit.UUID(), - Amount: amount, - URL: "https://docs.abacatepay.com/pages/webhooks#billing-paid", - Status: "PAID", - }, - }, - DevMode: true, - Event: "billing.paid", - } -} - -func MockPayoutEvent(isDone bool) *types.PayoutEvent { - status := "CANCELLED" - event := "payout.failed" - if isDone { - status = "COMPLETE" - event = "payout.done" - } - - amount := gofakeit.Number(1000, 50000) - id := fmt.Sprintf("evt_%s", gofakeit.LetterN(10)) - - return &types.PayoutEvent{ - ID: id, - Data: struct { - Transaction *types.EventTransaction `json:"transaction,omitempty"` - }{ - Transaction: &types.EventTransaction{ - ID: fmt.Sprintf("tran_%s", gofakeit.LetterN(16)), - Status: status, - DevMode: true, - ReceiptURL: "https://abacatepay.com/receipt/mock", - Kind: "WITHDRAW", - Amount: amount, - PlatformFee: 0, - ExternalID: gofakeit.UUID(), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - }, - }, - DevMode: true, - Event: event, - } -} diff --git a/internal/mock/helpers.go b/internal/mock/helpers.go deleted file mode 100644 index 176e2c8..0000000 --- a/internal/mock/helpers.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package mock... -package mock - -import ( - "math/rand" - "strings" -) - -func generateValidCPF(r *rand.Rand) string { - digits := make([]int, 11) - for i := range 9 { - digits[i] = r.Intn(10) - } - - sum := 0 - for i := range 9 { - sum += digits[i] * (10 - i) - } - digits[9] = calculateDigit(sum) - - sum = 0 - for i := range 10 { - sum += digits[i] * (11 - i) - } - digits[10] = calculateDigit(sum) - - var b strings.Builder - - for _, d := range digits { - b.WriteByte('0' + byte(d)) - } - - return b.String() -} - -func calculateDigit(sum int) int { - remainder := (sum * 10) % 11 - if remainder < 10 { - return remainder - } - return 0 -} diff --git a/internal/mock/payments.go b/internal/mock/payments.go deleted file mode 100644 index f855a98..0000000 --- a/internal/mock/payments.go +++ /dev/null @@ -1,54 +0,0 @@ -package mock - -import ( - "math/rand" - "time" - - "abacatepay-cli/internal/types" - - v1 "github.com/almeidazs/go-abacate-types/v1" - "github.com/brianvoe/gofakeit/v7" -) - -func CreatePixQRCodeMock() *v1.RESTPostCreateQRCodePixBody { - expires := 15 * 30 - desc := "salve" - _ = gofakeit.Seed(0) - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - return &v1.RESTPostCreateQRCodePixBody{ - Amount: gofakeit.Number(100, 10000), - ExpiresIn: &expires, - Description: &desc, - Customer: &v1.APICustomerMetadata{ - Name: gofakeit.Name(), - Email: gofakeit.Email(), - TaxID: generateValidCPF(r), - Cellphone: "11999999999", - }, - } -} - -func CreateCheckoutMock() *types.CreateCheckoutRequest { - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - return &types.CreateCheckoutRequest{ - ExternalID: gofakeit.UUID(), - Items: []types.Item{ - { - ID: gofakeit.UUID(), - Quantity: gofakeit.Number(1, 5), - }, - { - ID: gofakeit.UUID(), - Quantity: gofakeit.Number(1, 3), - }, - }, - Customer: &types.Customer{ - Name: gofakeit.Name(), - Email: gofakeit.Email(), - TaxID: generateValidCPF(r), - Cellphone: "11999999999", - }, - } -} diff --git a/internal/output/output.go b/internal/output/output.go index 0b39e3e..37a0209 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -4,7 +4,7 @@ import ( "fmt" "sync" - "abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/style" ) type Format string diff --git a/internal/payments/checkout.go b/internal/payments/checkout.go deleted file mode 100644 index f0a2642..0000000 --- a/internal/payments/checkout.go +++ /dev/null @@ -1,34 +0,0 @@ -package payments - -import ( - "fmt" - - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/types" -) - -func (s *Service) CreateCheckout(body *types.CreateCheckoutRequest) error { - var result types.CheckoutResponse - err := s.executeRequest( - s.Client.R().SetBody(body), - "POST", - s.BaseURL+"/v2/checkouts/create", - &result, - ) - if err != nil { - return err - } - - output.Print(output.Result{ - Title: "Checkout Created", - Fields: map[string]string{ - "ID": result.Data.ID, - "URL": result.Data.URL, - "Amount": fmt.Sprintf("R$ %.2f", float64(result.Data.Amount)/100), - "Status": result.Data.Status, - }, - Data: result, - }) - - return nil -} diff --git a/internal/payments/payments.go b/internal/payments/payments.go index 312a654..014a374 100644 --- a/internal/payments/payments.go +++ b/internal/payments/payments.go @@ -5,9 +5,9 @@ import ( "fmt" "net/http" - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/style" - "abacatepay-cli/internal/types" + "github.com/AbacatePay/abacatepay-cli/internal/output" + "github.com/AbacatePay/abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/types" "github.com/go-resty/resty/v2" ) diff --git a/internal/payments/pix_qrcode.go b/internal/payments/pix_qrcode.go index 7aa04d5..7eff657 100644 --- a/internal/payments/pix_qrcode.go +++ b/internal/payments/pix_qrcode.go @@ -1,44 +1,18 @@ package payments import ( - "abacatepay-cli/internal/output" - "abacatepay-cli/internal/types" - - v1 "github.com/almeidazs/go-abacate-types/v1" + "github.com/AbacatePay/abacatepay-cli/internal/output" + "github.com/AbacatePay/abacatepay-cli/internal/types" ) -func (s *Service) CreatePixQRCode(body *v1.RESTPostCreateQRCodePixBody, isTrigger bool) (string, error) { - var result types.PixResponse +func (s *Service) SimulateTransparentPayment(id string) error { + var result types.TransparentPaymentResponse err := s.executeRequest( - s.Client.R().SetBody(body), + s.Client.R(). + SetQueryParam("id", id). + SetBody(types.SimulatePaymentRequest{Metadata: map[string]any{}}), "POST", - s.BaseURL+"/v1"+v1.RouteCreatePIXQRCode, - &result, - ) - if err != nil { - return "", err - } - - if !isTrigger { - output.Print(output.Result{ - Title: "PIX Payment Created", - Fields: map[string]string{ - "ID": result.Data.ID, - "Status": "PENDING", - }, - Data: result, - }) - } - - return result.Data.ID, nil -} - -func (s *Service) CheckPixQRCode(id string) error { - var result types.PixResponse - err := s.executeRequest( - s.Client.R().SetQueryParam("id", id), - "GET", - s.BaseURL+v1.RouteCheckQRCodePIX, + s.BaseURL+"/v2/transparents/simulate-payment", &result, ) if err != nil { @@ -46,10 +20,11 @@ func (s *Service) CheckPixQRCode(id string) error { } output.Print(output.Result{ - Title: "PIX Status Check", + Title: "Payment Simulated", Fields: map[string]string{ - "ID": id, - "Status": result.Data.Status, + "ID": result.Data.ID, + "Status": result.Data.Status, + "DevMode": boolLabel(result.Data.DevMode), }, Data: result, }) @@ -57,28 +32,9 @@ func (s *Service) CheckPixQRCode(id string) error { return nil } -func (s *Service) SimulatePixQRCodePayment(id string, isTrigger bool) error { - var result types.PixResponse - err := s.executeRequest( - s.Client.R().SetQueryParam("id", id), - "POST", - s.BaseURL+"/v1"+v1.RouteSimulatePayment, - &result, - ) - if err != nil { - return err - } - - if !isTrigger { - output.Print(output.Result{ - Title: "PIX Payment Simulated", - Fields: map[string]string{ - "ID": result.Data.ID, - "Status": result.Data.Status, - }, - Data: result, - }) +func boolLabel(v bool) string { + if v { + return "true" } - - return nil + return "false" } diff --git a/internal/payments/utils.go b/internal/payments/utils.go index d97b00a..f080032 100644 --- a/internal/payments/utils.go +++ b/internal/payments/utils.go @@ -1,6 +1,6 @@ package payments -import "abacatepay-cli/internal/utils" +import "github.com/AbacatePay/abacatepay-cli/internal/utils" func ExecutePaymentAction(local, verbose bool, action func(*Service) error) error { deps, err := utils.SetupClient(local, verbose) diff --git a/internal/prompts/prompts.go b/internal/prompts/prompts.go deleted file mode 100644 index 6630a3e..0000000 --- a/internal/prompts/prompts.go +++ /dev/null @@ -1,82 +0,0 @@ -package prompts - -import ( - "fmt" - "strconv" - - "abacatepay-cli/internal/style" - "abacatepay-cli/internal/types" - - v1 "github.com/almeidazs/go-abacate-types/v1" -) - -func PromptForPIXQRCodeData(body *v1.RESTPostCreateQRCodePixBody) error { - var amountStr string - err := style.Input("Amount (in cents, e.g. 1000 for R$10.00)", "1000", &amountStr, func(s string) error { - if _, err := strconv.ParseInt(s, 10, 64); err != nil { - return fmt.Errorf("please enter a valid number") - } - return nil - }) - if err != nil { - return err - } - amount, _ := strconv.ParseInt(amountStr, 10, 64) - body.Amount = int(amount) - - var desc string - if err := style.Input("Product Description", "My purchase", &desc, nil); err != nil { - return err - } - body.Description = &desc - - if err := style.Input("Customer Name", "John Doe", &body.Customer.Name, nil); err != nil { - return err - } - - if err := style.Input("Customer Email", "john@example.com", &body.Customer.Email, nil); err != nil { - return err - } - - if err := style.Input("Customer TaxID (CPF/CNPJ)", "12345678909", &body.Customer.TaxID, nil); err != nil { - return err - } - - return nil -} - -func PromptForCheckout(body *types.CreateCheckoutRequest) error { - if err := style.Input("Customer Name", "John Doe", &body.Customer.Name, nil); err != nil { - return err - } - - if err := style.Input("Customer Email", "john@example.com", &body.Customer.Email, nil); err != nil { - return err - } - - if err := style.Input("Customer TaxID (CPF/CNPJ)", "12345678909", &body.Customer.TaxID, nil); err != nil { - return err - } - var productID string - if err := style.Input("Product ID", "prod_abc123xyz", &productID, nil); err != nil { - return err - } - - var qtdStr string - if err := style.Input("Product Quantity", "1", &qtdStr, nil); err != nil { - return err - } - qtd, err := strconv.Atoi(qtdStr) - if err != nil { - return err - } - - body.Items = []types.Item{ - { - ID: productID, - Quantity: qtd, - }, - } - - return nil -} diff --git a/internal/scaffold/config.go b/internal/scaffold/config.go deleted file mode 100644 index fcff6cc..0000000 --- a/internal/scaffold/config.go +++ /dev/null @@ -1,68 +0,0 @@ -// Package scaffold provides project scaffolding functionality for AbacatePay CLI. -// It handles cloning templates, composing project layers, and setting up new projects. -package scaffold - -import ( - "fmt" - "strings" -) - -// Config holds the scaffold configuration for creating a new project. -type Config struct { - ProjectName string // Name of the project (directory name) - Framework string // Framework to use: "next" or "elysia" - Linter string // Linter to use: "eslint" or "biome" - BetterAuth bool // Whether to include BetterAuth configuration -} - -// Validate checks if the configuration is valid. -func (c Config) Validate() error { - if err := ValidateProjectName(c.ProjectName); err != nil { - return err - } - - validFrameworks := map[string]bool{"next": true, "elysia": true} - if !validFrameworks[c.Framework] { - return fmt.Errorf("invalid framework: %s (must be 'next' or 'elysia')", c.Framework) - } - - validLinters := map[string]bool{"eslint": true, "biome": true} - if !validLinters[c.Linter] { - return fmt.Errorf("invalid linter: %s (must be 'eslint' or 'biome')", c.Linter) - } - - return nil -} - -// ValidateProjectName checks if the project name is valid. -// It ensures the name is not empty and doesn't contain invalid characters. -func ValidateProjectName(name string) error { - if name == "" { - return fmt.Errorf("project name is required") - } - - // Check for invalid characters that are not allowed in directory names - invalidChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"} - for _, char := range invalidChars { - if strings.Contains(name, char) { - return fmt.Errorf("project name cannot contain '%s'", char) - } - } - - return nil -} - -// GetNextSteps returns the list of commands the user should run after project creation. -func (c Config) GetNextSteps() []string { - steps := []string{ - fmt.Sprintf("cd %s", c.ProjectName), - "bun install", - "cp .env.example .env", - } - - // Both frameworks use the same commands for now - steps = append(steps, "bun run db:push") - steps = append(steps, "bun run dev") - - return steps -} diff --git a/internal/scaffold/fs.go b/internal/scaffold/fs.go deleted file mode 100644 index 8890f3c..0000000 --- a/internal/scaffold/fs.go +++ /dev/null @@ -1,72 +0,0 @@ -package scaffold - -import ( - "fmt" - "io" - "os" - "path/filepath" -) - -func CopyFile(src, dst string) error { - srcFile, err := os.Open(src) - if err != nil { - return fmt.Errorf("failed to open source file %s: %w", src, err) - } - defer srcFile.Close() - - err = os.MkdirAll(filepath.Dir(dst), 0o755) - if err != nil { - return fmt.Errorf("failed to create directory for %s: %w", dst, err) - } - - dstFile, err := os.Create(dst) - if err != nil { - return fmt.Errorf("failed to create destination file %s: %w", dst, err) - } - defer dstFile.Close() - - if _, err := io.Copy(dstFile, srcFile); err != nil { - return fmt.Errorf("failed to copy file %s to %s: %w", src, dst, err) - } - - return nil -} - -func CopyDir(src, dst string) error { - return filepath.Walk(src, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() && (info.Name() == "node_modules" || info.Name() == ".git") { - return filepath.SkipDir - } - - relPath, err := filepath.Rel(src, path) - if err != nil { - return fmt.Errorf("failed to calculate relative path: %w", err) - } - - dstPath := filepath.Join(dst, relPath) - - if info.IsDir() { - return os.MkdirAll(dstPath, info.Mode()) - } - - return CopyFile(path, dstPath) - }) -} - -func EnsureDir(path string, perm os.FileMode) error { - return os.MkdirAll(path, perm) -} - -func DirExists(path string) bool { - info, err := os.Stat(path) - return err == nil && info.IsDir() -} - -func FileExists(path string) bool { - info, err := os.Stat(path) - return err == nil && !info.IsDir() -} \ No newline at end of file diff --git a/internal/scaffold/git.go b/internal/scaffold/git.go deleted file mode 100644 index 5edf9c6..0000000 --- a/internal/scaffold/git.go +++ /dev/null @@ -1,24 +0,0 @@ -package scaffold - -import ( - "fmt" - "os" - "os/exec" -) - -// GitRepositoryURL is the URL of the templates repository. -const GitRepositoryURL = "https://github.com/albuquerquesz/abacatepay-templates.git" - -// GitClone clones the templates repository into the specified directory. -// It performs a shallow clone (--depth 1) to save bandwidth and time. -func GitClone(destDir string) error { - cmd := exec.Command("git", "clone", "--depth", "1", GitRepositoryURL, destDir) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - if err := cmd.Run(); err != nil { - return fmt.Errorf("git clone failed: %w", err) - } - - return nil -} diff --git a/internal/scaffold/package.go b/internal/scaffold/package.go deleted file mode 100644 index 9ab3930..0000000 --- a/internal/scaffold/package.go +++ /dev/null @@ -1,117 +0,0 @@ -package scaffold - -import ( - "encoding/json" - "fmt" - "maps" - "os" -) - -// PackageJSON represents the structure of a package.json file. -type PackageJSON struct { - Name string `json:"name"` - Version string `json:"version"` - Private bool `json:"private,omitempty"` - Scripts map[string]string `json:"scripts,omitempty"` - Dependencies map[string]string `json:"dependencies,omitempty"` - DevDependencies map[string]string `json:"devDependencies,omitempty"` -} - -// ReadPackageJSON reads and parses a package.json file from the given path. -func ReadPackageJSON(path string) (*PackageJSON, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("failed to read package.json: %w", err) - } - - var pkg PackageJSON - if err := json.Unmarshal(data, &pkg); err != nil { - return nil, fmt.Errorf("failed to parse package.json: %w", err) - } - - return &pkg, nil -} - -// Write writes the package.json to the specified path. -func (p *PackageJSON) Write(path string) error { - data, err := json.MarshalIndent(p, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal package.json: %w", err) - } - - if err := os.WriteFile(path, data, 0o644); err != nil { - return fmt.Errorf("failed to write package.json: %w", err) - } - - return nil -} - -// MergeDependencies merges dependencies from another package.json into this one. -// The source dependencies will be added to the destination, overwriting any existing ones. -func (p *PackageJSON) MergeDependencies(source *PackageJSON) { - if source.Dependencies != nil { - if p.Dependencies == nil { - p.Dependencies = make(map[string]string) - } - maps.Copy(p.Dependencies, source.Dependencies) - } - - if source.DevDependencies != nil { - if p.DevDependencies == nil { - p.DevDependencies = make(map[string]string) - } - maps.Copy(p.DevDependencies, source.DevDependencies) - } -} - -// MergeScripts merges scripts from another package.json into this one. -func (p *PackageJSON) MergeScripts(source *PackageJSON) { - if source.Scripts != nil { - if p.Scripts == nil { - p.Scripts = make(map[string]string) - } - maps.Copy(p.Scripts, source.Scripts) - } -} - -// LoadAndMerge reads a package.json file and merges its contents into this one. -func (p *PackageJSON) LoadAndMerge(path string) error { - source, err := ReadPackageJSON(path) - if err != nil { - if os.IsNotExist(err) { - return nil // File doesn't exist, nothing to merge - } - return err - } - - p.MergeDependencies(source) - p.MergeScripts(source) - return nil -} - -// LoadScriptsAndMerge reads a scripts.json file and merges its scripts into this package.json. -func (p *PackageJSON) LoadScriptsAndMerge(path string) error { - data, err := os.ReadFile(path) - if err != nil { - if os.IsNotExist(err) { - return nil // File doesn't exist, nothing to merge - } - return fmt.Errorf("failed to read scripts file: %w", err) - } - - var scriptsWrapper struct { - Scripts map[string]string `json:"scripts"` - } - if err := json.Unmarshal(data, &scriptsWrapper); err != nil { - return fmt.Errorf("failed to parse scripts file: %w", err) - } - - if scriptsWrapper.Scripts != nil { - if p.Scripts == nil { - p.Scripts = make(map[string]string) - } - maps.Copy(p.Scripts, scriptsWrapper.Scripts) - } - - return nil -} diff --git a/internal/scaffold/scaffold.go b/internal/scaffold/scaffold.go deleted file mode 100644 index 748ef0d..0000000 --- a/internal/scaffold/scaffold.go +++ /dev/null @@ -1,190 +0,0 @@ -package scaffold - -import ( - "fmt" - "os" - "path/filepath" -) - -type ProjectBuilder struct { - config Config - templatesDir string - projectPath string -} - -func NewProjectBuilder(cfg Config, targetDir string) (*ProjectBuilder, error) { - if err := cfg.Validate(); err != nil { - return nil, err - } - - return &ProjectBuilder{ - config: cfg, - projectPath: filepath.Join(targetDir, cfg.ProjectName), - }, nil -} - -func (pb *ProjectBuilder) Build() error { - tempDir, err := os.MkdirTemp("", "abacate-templates-*") - if err != nil { - return fmt.Errorf("failed to create temp directory: %w", err) - } - defer os.RemoveAll(tempDir) - - if err := pb.cloneTemplates(tempDir); err != nil { - return err - } - - pb.templatesDir = tempDir - - if err := EnsureDir(pb.projectPath, 0o755); err != nil { - return fmt.Errorf("failed to create project directory: %w", err) - } - - if err := pb.applyBaseTemplate(); err != nil { - return err - } - - if err := pb.applyLinter(); err != nil { - return err - } - - if err := pb.applyFeatures(); err != nil { - return err - } - - if err := pb.finalizePackageJSON(); err != nil { - return err - } - - return nil -} - -func (pb *ProjectBuilder) cloneTemplates(tempDir string) error { - if err := GitClone(tempDir); err != nil { - return fmt.Errorf("failed to clone templates repository: %w", err) - } - return nil -} - -func (pb *ProjectBuilder) applyBaseTemplate() error { - basePath := filepath.Join(pb.templatesDir, "base", pb.config.Framework) - - if !DirExists(basePath) { - return fmt.Errorf("base template not found for framework: %s", pb.config.Framework) - } - - if err := CopyDir(basePath, pb.projectPath); err != nil { - return fmt.Errorf("failed to copy base template: %w", err) - } - - return nil -} - -func (pb *ProjectBuilder) applyLinter() error { - linterDir := filepath.Join(pb.templatesDir, "linters", pb.config.Linter) - - switch pb.config.Linter { - case "eslint": - if err := pb.applyESLint(linterDir); err != nil { - return err - } - case "biome": - if err := pb.applyBiome(linterDir); err != nil { - return err - } - } - - return nil -} - -func (pb *ProjectBuilder) applyESLint(linterDir string) error { - configFile := "eslint.config.elysia.js" - if pb.config.Framework == "next" { - configFile = "eslint.config.next.js" - } - - src := filepath.Join(linterDir, configFile) - dst := filepath.Join(pb.projectPath, "eslint.config.js") - - if err := CopyFile(src, dst); err != nil { - return fmt.Errorf("failed to copy ESLint config: %w", err) - } - - return nil -} - -func (pb *ProjectBuilder) applyBiome(linterDir string) error { - src := filepath.Join(linterDir, "biome.json") - dst := filepath.Join(pb.projectPath, "biome.json") - - if err := CopyFile(src, dst); err != nil { - return fmt.Errorf("failed to copy Biome config: %w", err) - } - - return nil -} - -func (pb *ProjectBuilder) applyFeatures() error { - if pb.config.BetterAuth { - if err := pb.applyBetterAuth(); err != nil { - return err - } - } - - return nil -} - -func (pb *ProjectBuilder) applyBetterAuth() error { - authDir := filepath.Join(pb.templatesDir, "features", "betterauth", pb.config.Framework) - - if !DirExists(authDir) { - return fmt.Errorf("betterauth template not found for framework: %s", pb.config.Framework) - } - - if err := CopyDir(authDir, pb.projectPath); err != nil { - return fmt.Errorf("failed to copy better-auth files: %w", err) - } - - return nil -} - -func (pb *ProjectBuilder) finalizePackageJSON() error { - mainPackagePath := filepath.Join(pb.projectPath, "package.json") - - pkg, err := ReadPackageJSON(mainPackagePath) - if err != nil { - return fmt.Errorf("failed to read package.json: %w", err) - } - - linterDir := filepath.Join(pb.templatesDir, "linters", pb.config.Linter) - if err := pkg.LoadAndMerge(filepath.Join(linterDir, "package.json")); err != nil { - return fmt.Errorf("failed to merge linter dependencies: %w", err) - } - if err := pkg.LoadScriptsAndMerge(filepath.Join(linterDir, "scripts.json")); err != nil { - return fmt.Errorf("failed to merge linter scripts: %w", err) - } - - if pb.config.BetterAuth { - authDir := filepath.Join(pb.templatesDir, "features", "betterauth", pb.config.Framework) - if err := pkg.LoadAndMerge(filepath.Join(authDir, "package.json")); err != nil { - return fmt.Errorf("failed to merge better-auth dependencies: %w", err) - } - } - - pkg.Name = pb.config.ProjectName - - if err := pkg.Write(mainPackagePath); err != nil { - return fmt.Errorf("failed to write package.json: %w", err) - } - - return nil -} - -func ScaffoldProject(cfg Config, targetDir string) error { - builder, err := NewProjectBuilder(cfg, targetDir) - if err != nil { - return err - } - - return builder.Build() -} diff --git a/internal/types/types.go b/internal/types/types.go index 3423f62..f12c493 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -1,48 +1,17 @@ package types -type Customer struct { - Name string `json:"name,omitempty"` - Cellphone string `json:"cellphone,omitempty"` - Email string `json:"email,omitempty"` - TaxID string `json:"taxId,omitempty"` -} - -type CreateCheckoutRequest struct { - Items []Item `json:"items"` - Method string `json:"method,omitempty"` // PIX | CARD (default: PIX) - ReturnURL string `json:"returnUrl,omitempty"` - CompletionURL string `json:"completionUrl,omitempty"` - CustomerID string `json:"customerId,omitempty"` - Customer *Customer `json:"customer,omitempty"` - Coupons []string `json:"coupons,omitempty"` - ExternalID string `json:"externalId,omitempty"` -} - -type Item struct { - ID string `json:"id"` - Quantity int `json:"quantity"` -} - -type CheckoutResponse struct { - Data struct { - ID string `json:"id"` - URL string `json:"url"` - Status string `json:"status"` - Amount int `json:"amount"` - } `json:"data"` +type User struct { + Name string + Email string } -type PixResponse struct { +type StoreResponse struct { Data struct { - ID string `json:"id"` - BRCode string `json:"brCode"` - Status string `json:"status"` + ID string `json:"id"` + Name string `json:"name"` } `json:"data"` -} - -type User struct { - Name string - Email string + Error string `json:"error"` + Success any `json:"success"` } type DeviceLoginResponse struct { @@ -57,3 +26,26 @@ type TokenResponse struct { type APIError struct { Message string `json:"error"` } + +type SimulatePaymentRequest struct { + Metadata map[string]any `json:"metadata"` +} + +type TransparentPaymentResponse struct { + Data struct { + ID string `json:"id"` + Amount int `json:"amount"` + Status string `json:"status"` + DevMode bool `json:"devMode"` + BRCode string `json:"brCode"` + BRCodeBase64 string `json:"brCodeBase64"` + PlatformFee int `json:"platformFee"` + ReceiptURL string `json:"receiptUrl"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + ExpiresAt string `json:"expiresAt"` + Metadata map[string]any `json:"metadata"` + } `json:"data"` + Error string `json:"error"` + Success any `json:"success"` +} diff --git a/internal/utils/listener.go b/internal/utils/listener.go index 587a8e4..4e00cf8 100644 --- a/internal/utils/listener.go +++ b/internal/utils/listener.go @@ -7,7 +7,7 @@ import ( "log/slog" "os" - "abacatepay-cli/internal/webhook" + "github.com/AbacatePay/abacatepay-cli/internal/webhook" ) func StartListener(params *StartListenerParams) error { diff --git a/internal/utils/network.go b/internal/utils/network.go index 05ec840..e044e67 100644 --- a/internal/utils/network.go +++ b/internal/utils/network.go @@ -8,7 +8,7 @@ import ( "runtime" "time" - "abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/style" ) const DefaultForwardURL = "http://localhost:3000/webhooks/abacatepay" diff --git a/internal/utils/utils.go b/internal/utils/utils.go index e848044..9164db5 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -6,11 +6,11 @@ import ( "fmt" "log/slog" - "abacatepay-cli/internal/auth" - "abacatepay-cli/internal/client" - "abacatepay-cli/internal/config" - "abacatepay-cli/internal/logger" - "abacatepay-cli/internal/store" + "github.com/AbacatePay/abacatepay-cli/internal/auth" + "github.com/AbacatePay/abacatepay-cli/internal/client" + "github.com/AbacatePay/abacatepay-cli/internal/config" + "github.com/AbacatePay/abacatepay-cli/internal/logger" + "github.com/AbacatePay/abacatepay-cli/internal/store" "github.com/go-resty/resty/v2" ) diff --git a/internal/version/update.go b/internal/version/update.go deleted file mode 100644 index 1796442..0000000 --- a/internal/version/update.go +++ /dev/null @@ -1,43 +0,0 @@ -package version - -import ( - "context" - "fmt" - "os" - - "abacatepay-cli/internal/style" - - "github.com/creativeprojects/go-selfupdate" -) - -func CheckUpdate(ctx context.Context, currentVersion string) (*selfupdate.Release, bool, error) { - slug := "AbacatePay/abacatepay-cli" - latest, found, err := selfupdate.DetectLatest(ctx, selfupdate.ParseSlug(slug)) - if err != nil { - return nil, false, err - } - - if !found || currentVersion == "" || latest.LessOrEqual(currentVersion) { - return nil, false, nil - } - - return latest, true, nil -} - -func ShowUpdate(currentVersion string) { - latest, found, _ := CheckUpdate(context.Background(), currentVersion) - - if !found { - return - } - - msg := fmt.Sprintf( - "🥑 %s %s\n Current: %s\n\n To update, run:\n %s", - style.TitleStyle.Render("Update available:"), - style.VersionStyle.Render(latest.Version()), - currentVersion, - style.CommandStyle.Render("abacatepay update"), - ) - - fmt.Fprintln(os.Stderr, style.BoxStyle.Render(msg)) -} diff --git a/internal/webhook/base_listener.go b/internal/webhook/base_listener.go index be5335e..f05567a 100644 --- a/internal/webhook/base_listener.go +++ b/internal/webhook/base_listener.go @@ -7,8 +7,8 @@ import ( "sync" "time" - "abacatepay-cli/internal/config" - "abacatepay-cli/internal/ws" + "github.com/AbacatePay/abacatepay-cli/internal/config" + "github.com/AbacatePay/abacatepay-cli/internal/ws" "github.com/gorilla/websocket" ) diff --git a/internal/webhook/listener.go b/internal/webhook/listener.go index 4e5cdb0..8339daf 100644 --- a/internal/webhook/listener.go +++ b/internal/webhook/listener.go @@ -9,9 +9,9 @@ import ( "net/http" "time" - "abacatepay-cli/internal/crypto" - "abacatepay-cli/internal/style" - "abacatepay-cli/internal/ws" + "github.com/AbacatePay/abacatepay-cli/internal/crypto" + "github.com/AbacatePay/abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/ws" "github.com/gorilla/websocket" "golang.org/x/sync/errgroup" diff --git a/internal/webhook/tail.go b/internal/webhook/tail.go index e02924e..6ab1ba6 100644 --- a/internal/webhook/tail.go +++ b/internal/webhook/tail.go @@ -7,9 +7,9 @@ import ( "fmt" "log/slog" - "abacatepay-cli/internal/config" - "abacatepay-cli/internal/style" - "abacatepay-cli/internal/ws" + "github.com/AbacatePay/abacatepay-cli/internal/config" + "github.com/AbacatePay/abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/ws" "github.com/gorilla/websocket" ) diff --git a/internal/webhook/webhook.go b/internal/webhook/webhook.go index 41717f2..9e58553 100644 --- a/internal/webhook/webhook.go +++ b/internal/webhook/webhook.go @@ -6,7 +6,7 @@ import ( "log/slog" "time" - "abacatepay-cli/internal/config" + "github.com/AbacatePay/abacatepay-cli/internal/config" "github.com/go-resty/resty/v2" ) diff --git a/internal/ws/connection.go b/internal/ws/connection.go index 89342d1..6909d8f 100644 --- a/internal/ws/connection.go +++ b/internal/ws/connection.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "abacatepay-cli/internal/style" + "github.com/AbacatePay/abacatepay-cli/internal/style" "github.com/gorilla/websocket" ) diff --git a/main.go b/main.go index 54409f2..0e72c3d 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,8 @@ import ( "fmt" "os" - "abacatepay-cli/cmd" - "abacatepay-cli/internal/logger" + "github.com/AbacatePay/abacatepay-cli/cmd" + "github.com/AbacatePay/abacatepay-cli/internal/logger" ) func main() {