/**
* Go SDK Generator
* Generates Go client SDKs for x402-enabled APIs
*
* @author nich
* @github github.com/nirholas
* @license Apache-2.0
*/
import {
SDKConfig,
SDKGenerationResult,
toPascalCase,
extractPathParams,
} from './types.js';
/**
* Generate Go method name from HTTP method and path
*/
function routeToGoMethodName(method: string, path: string): string {
const parts = path.split('/').filter(p => p && !p.startsWith(':'));
const name = parts.map(p => toPascalCase(p)).join('');
return toPascalCase(method) + name;
}
/**
* Generate Go route methods
*/
function generateRouteMethodsGo(routes: Record<string, string>): string {
const methods: string[] = [];
for (const [route, price] of Object.entries(routes)) {
const [method, path] = route.split(' ');
const methodName = routeToGoMethodName(method, path);
const params = extractPathParams(path);
const paramList = params.map(p => `${p} string`).join(', ');
const bodyParam = ['POST', 'PUT', 'PATCH'].includes(method) ? 'body interface{}' : '';
const fullParams = [paramList, bodyParam].filter(Boolean).join(', ');
const pathWithParams = params.reduce(
(p, param) => p.replace(`:${param}`, `%s`),
path
);
const pathArgs = params.length > 0
? `, ${params.join(', ')}`
: '';
methods.push(`// ${methodName} - ${method} ${path}
// Price: $${price}
func (c *Client) ${methodName}(${fullParams}) ([]byte, error) {
path := fmt.Sprintf("${pathWithParams}"${pathArgs})
return c.makeRequest("${method}", path, "${price}", ${bodyParam ? 'body' : 'nil'})
}`);
}
return methods.join('\n\n');
}
/**
* Generate a Go SDK for the given configuration
*/
export function generateGoSDK(config: SDKConfig): SDKGenerationResult {
const packageName = config.apiName.toLowerCase().replace(/[^a-z0-9]/g, '');
const code = `// Package ${packageName} provides a client for the ${config.apiName} API
//
// This client handles x402 payment authentication automatically.
//
// Example:
//
// client := ${packageName}.NewClient(${packageName}.WithPayerKey("0x..."))
// result, err := client.GetData("123")
//
// Auto-generated by x402-deploy
// See https://x402.org
package ${packageName}
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client is the ${config.apiName} API client
type Client struct {
// APIUrl is the base URL of the API
APIUrl string
// FacilitatorURL is the x402 facilitator service URL
FacilitatorURL string
// PayerKey is the private key for automatic payments
PayerKey string
// Wallet is the recipient wallet address
Wallet string
// HTTPClient is the HTTP client to use for requests
HTTPClient *http.Client
}
// Option is a functional option for configuring the client
type Option func(*Client)
// NewClient creates a new ${config.apiName} client
func NewClient(options ...Option) *Client {
client := &Client{
APIUrl: "${config.apiUrl}",
FacilitatorURL: "${config.facilitator}",
Wallet: "${config.wallet}",
HTTPClient: &http.Client{
Timeout: 30 * time.Second,
},
}
for _, opt := range options {
opt(client)
}
return client
}
// WithFacilitator sets a custom facilitator URL
func WithFacilitator(url string) Option {
return func(c *Client) {
c.FacilitatorURL = url
}
}
// WithPayerKey sets the payer private key for automatic payments
func WithPayerKey(key string) Option {
return func(c *Client) {
c.PayerKey = key
}
}
// WithHTTPClient sets a custom HTTP client
func WithHTTPClient(httpClient *http.Client) Option {
return func(c *Client) {
c.HTTPClient = httpClient
}
}
// WithAPIUrl sets a custom API URL
func WithAPIUrl(url string) Option {
return func(c *Client) {
c.APIUrl = url
}
}
// PaymentProof represents an x402 payment proof
type PaymentProof struct {
Signature string \`json:"signature"\`
Timestamp int64 \`json:"timestamp"\`
Amount string \`json:"amount"\`
Network string \`json:"network"\`
}
// APIError represents an API error response
type APIError struct {
StatusCode int
Message string
}
func (e *APIError) Error() string {
return fmt.Sprintf("API error %d: %s", e.StatusCode, e.Message)
}
type paymentProofRequest struct {
Recipient string \`json:"recipient"\`
Amount string \`json:"amount"\`
Network string \`json:"network"\`
Route string \`json:"route"\`
PayerKey string \`json:"payerKey,omitempty"\`
}
type paymentProofResponse struct {
Proof string \`json:"proof"\`
}
func (c *Client) getPaymentProof(route, price string) (string, error) {
reqBody := paymentProofRequest{
Recipient: c.Wallet,
Amount: price,
Network: "eip155:8453",
Route: route,
PayerKey: c.PayerKey,
}
body, err := json.Marshal(reqBody)
if err != nil {
return "", fmt.Errorf("failed to marshal request: %w", err)
}
resp, err := c.HTTPClient.Post(
c.FacilitatorURL+"/create-payment",
"application/json",
bytes.NewBuffer(body),
)
if err != nil {
return "", fmt.Errorf("failed to get payment proof: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
respBody, _ := io.ReadAll(resp.Body)
return "", &APIError{StatusCode: resp.StatusCode, Message: string(respBody)}
}
var proofResp paymentProofResponse
if err := json.NewDecoder(resp.Body).Decode(&proofResp); err != nil {
return "", fmt.Errorf("failed to decode response: %w", err)
}
return proofResp.Proof, nil
}
func (c *Client) makeRequest(method, path, price string, body interface{}) ([]byte, error) {
proof, err := c.getPaymentProof(method+" "+path, price)
if err != nil {
return nil, fmt.Errorf("failed to get payment proof: %w", err)
}
var reqBody io.Reader
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to marshal body: %w", err)
}
reqBody = bytes.NewBuffer(jsonBody)
}
req, err := http.NewRequest(method, c.APIUrl+path, reqBody)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-payment", proof)
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode >= 400 {
return nil, &APIError{StatusCode: resp.StatusCode, Message: string(respBody)}
}
return respBody, nil
}
// GetDiscovery retrieves the x402 discovery document
func (c *Client) GetDiscovery() ([]byte, error) {
resp, err := c.HTTPClient.Get(c.APIUrl + "/.well-known/x402")
if err != nil {
return nil, fmt.Errorf("failed to get discovery: %w", err)
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
${generateRouteMethodsGo(config.routes)}
`;
return {
language: 'go',
code,
extension: 'go',
directory: 'go'
};
}