naco-api/cmd/check-facilitator/main.go

138 lines
4.1 KiB
Go

package main
import (
"context"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"github.com/nacorid/naco-api/internal/server"
)
func main() {
facilitatorURL := os.Getenv("X402_FACILITATOR_URL")
if facilitatorURL == "" {
facilitatorURL = "https://api.cdp.coinbase.com/platform/v2/x402"
}
isMogami := strings.Contains(facilitatorURL, "mogami.tech")
isPayAI := strings.Contains(facilitatorURL, "payai.network")
isCDP := strings.Contains(facilitatorURL, "cdp.coinbase.com")
var req *http.Request
var err error
if isMogami || isPayAI {
// Mogami and PayAI facilitators don't require auth
req, err = http.NewRequestWithContext(context.Background(), "GET", facilitatorURL+"/supported", nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create request: %v\n", err)
os.Exit(1)
}
if isPayAI {
fmt.Println("Using PayAI facilitator (no auth)")
} else {
fmt.Println("Using Mogami facilitator (no auth)")
}
} else if isCDP {
keyName := os.Getenv("CDP_API_KEY_ID")
privateKeyPEM := os.Getenv("CDP_API_SECRET")
if keyName == "" || privateKeyPEM == "" {
fmt.Fprintf(os.Stderr, "CDP_API_KEY_ID and CDP_API_SECRET must be set for CDP facilitator\n")
os.Exit(1)
}
// Debug: print key info
fmt.Fprintf(os.Stderr, "DEBUG: keyName=%s, secretLen=%d\n", keyName, len(privateKeyPEM))
fmt.Fprintf(os.Stderr, "DEBUG: secretStart=%s\n", privateKeyPEM[:min(80, len(privateKeyPEM))])
fmt.Fprintf(os.Stderr, "DEBUG: hasLiteralNewline=%v, hasActualNewline=%v\n",
strings.Contains(privateKeyPEM, "\\n"),
strings.Contains(privateKeyPEM, "\n"))
// Test key parsing manually first
processedPEM := strings.ReplaceAll(privateKeyPEM, "\\n", "\n")
fmt.Fprintf(os.Stderr, "DEBUG: after replaceAll, startsWith=%s\n", processedPEM[:min(50, len(processedPEM))])
block, _ := pem.Decode([]byte(processedPEM))
if block != nil {
fmt.Fprintf(os.Stderr, "DEBUG: pem block type=%s, bytes=%d\n", block.Type, len(block.Bytes))
if strings.Contains(block.Type, "EC PRIVATE KEY") {
if _, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
fmt.Fprintf(os.Stderr, "DEBUG: parsed as SEC1 EC key\n")
} else if parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
fmt.Fprintf(os.Stderr, "DEBUG: parsed as PKCS8 key, type=%T\n", parsed)
}
}
}
auth, err := server.NewCDPAuth(keyName, privateKeyPEM)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create CDP auth: %v\n", err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "DEBUG: auth created, signingMethod=%v\n", auth.GetSigningMethod())
headers, err := auth.GetAuthHeaders(context.Background())
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to get auth headers: %v\n", err)
os.Exit(1)
}
// Debug: print generated tokens
for name, header := range map[string]string{
"Verify": headers.Verify["Authorization"],
"Settle": headers.Settle["Authorization"],
"Supported": headers.Supported["Authorization"],
} {
if len(header) > 7 {
token := header[7:] // strip "Bearer "
if parts := strings.Split(token, "."); len(parts) == 3 {
if payload, err := base64.RawURLEncoding.DecodeString(parts[1]); err == nil {
fmt.Fprintf(os.Stderr, "DEBUG: %s token claims: %s\n", name, string(payload))
}
}
}
}
req, err = http.NewRequestWithContext(context.Background(), "GET", facilitatorURL+"/supported", nil)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to create request: %v\n", err)
os.Exit(1)
}
for k, v := range headers.Supported {
req.Header.Set(k, v)
}
fmt.Println("Using CDP facilitator (with auth)")
} else {
fmt.Fprintf(os.Stderr, "Unknown facilitator URL: %s\n", facilitatorURL)
os.Exit(1)
}
httpClient := &http.Client{
Timeout: 30 * time.Second,
}
resp, err := httpClient.Do(req)
if err != nil {
fmt.Fprintf(os.Stderr, "Request failed: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
maxBytes := int64(5 * 1024 * 1024)
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBytes))
if err != nil {
fmt.Printf("failed reading response body: %v", err)
}
fmt.Printf("Status: %d\n", resp.StatusCode)
fmt.Printf("Response: %s\n", body)
}