Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions internal/qbo/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package client

// Client will hold HTTP behavior for QBO requests.
type Client struct {
cfg Config
}

// New constructs a Client with the provided configuration.
func New(cfg Config) *Client {
return &Client{cfg: cfg}
}
27 changes: 27 additions & 0 deletions internal/qbo/client/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Package client provides a minimal QuickBooks Online HTTP client scaffold.
package client

import "net/http"

// TokenSource supplies bearer tokens for authenticated QBO requests.
type TokenSource interface {
AccessToken() (string, error)
}

// Config holds construction parameters for the QBO client.
type Config struct {
// BaseURL is the QBO company base URL (sandbox or production).
BaseURL string
// RealmID identifies the QuickBooks company.
RealmID string
// MinorVersion sets the optional Intuit minorversion query parameter.
MinorVersion int
// HTTPClient is the transport; if nil, http.DefaultClient is used.
HTTPClient *http.Client
// TokenSource provides bearer tokens for requests.
TokenSource TokenSource
// RetryPolicy controls retry behavior for transient errors.
RetryPolicy RetryPolicy
// UserAgent is sent on outbound requests.
UserAgent string
}
32 changes: 32 additions & 0 deletions internal/qbo/client/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package client

import "fmt"

// ErrUnauthorized indicates the request was not authorized (e.g., token expired).
var ErrUnauthorized = fmt.Errorf("unauthorized")

// ErrRateLimited indicates the request hit a QBO rate limit.
var ErrRateLimited = fmt.Errorf("rate limited")

// ErrNotFound indicates the requested resource was not found.
var ErrNotFound = fmt.Errorf("not found")

// APIError wraps a QBO HTTP response with useful metadata.
type APIError struct {
StatusCode int // HTTP status code returned by QBO.
IntuitTID string // Intuit request ID header for tracing.
Fault []byte // Raw fault payload if provided.
Body []byte // Raw response body for debugging.
Err error // Underlying error, if any.
}

func (e *APIError) Error() string {
if e.Err != nil {
return e.Err.Error()
}
return fmt.Sprintf("qbo api error status=%d", e.StatusCode)
}

func (e *APIError) Unwrap() error {
return e.Err
}
9 changes: 9 additions & 0 deletions internal/qbo/client/pagination.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package client

// Page represents a single paginated response payload.
type Page struct {
StartPosition int
MaxResults int
TotalCount int
Entities map[string]any
}
19 changes: 19 additions & 0 deletions internal/qbo/client/retry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package client

import "time"

// RetryPolicy defines retry behavior for transient failures.
type RetryPolicy struct {
MaxAttempts int
BaseDelay time.Duration
MaxDelay time.Duration
JitterPercent float64
}

// DefaultRetryPolicy provides conservative defaults for QBO.
var DefaultRetryPolicy = RetryPolicy{
MaxAttempts: 3,
BaseDelay: 500 * time.Millisecond,
MaxDelay: 30 * time.Second,
JitterPercent: 0.2,
}
10 changes: 10 additions & 0 deletions internal/qbo/tokens/source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Package tokens provides token persistence and adapters for QBO clients.
package tokens

// Source is a placeholder adapter that will wrap Service for refresh-aware tokens.
type Source struct{}

// AccessToken is a stub implementation to satisfy the interface scaffold.
func (s *Source) AccessToken() (string, error) {
return "", nil
}
Loading