feat(backend): update Square integration, validators, and image validation

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-06-18 16:26:47 +01:00
co-authored by Sisyphus
parent bc6a5461c1
commit 2dbb1486b0
7 changed files with 91 additions and 34 deletions
+1 -1
View File
@@ -50,4 +50,4 @@ func (p *ProdClient) GetCardsOnFile(ctx context.Context, userID string) ([]CardO
func (p *ProdClient) DeleteCardOnFile(ctx context.Context, cardID string) error {
return errors.New("square payments not yet configured — set SQUARE_ACCESS_TOKEN and SQUARE_LOCATION_ID in .env")
}
}
+19 -11
View File
@@ -14,13 +14,21 @@ import (
var Client SquareClient
var isTesting = os.Getenv("GO_TESTING") == "1"
func mockSleep(d time.Duration) {
if !isTesting {
time.Sleep(d)
}
}
type MockClient struct {
mu sync.RWMutex
cards map[string]map[string]*CardOnFile
checkouts map[string]*CheckoutResult
payments map[string]*PaymentResult
refunds map[string]*RefundResult
completed map[string]*PaymentResult
mu sync.RWMutex
cards map[string]map[string]*CardOnFile
checkouts map[string]*CheckoutResult
payments map[string]*PaymentResult
refunds map[string]*RefundResult
completed map[string]*PaymentResult
}
type devProdClient struct{}
@@ -72,7 +80,7 @@ func NewDevClient() SquareClient {
func (m *MockClient) CreatePayment(ctx context.Context, req CreatePaymentReq) (*PaymentResult, error) {
log.Printf("[SQUARE-MOCK] CreatePayment: amount=%d, reference=%s", req.Amount, req.ReferenceID)
time.Sleep(1 * time.Second)
mockSleep(1 * time.Second)
m.mu.Lock()
defer m.mu.Unlock()
@@ -110,7 +118,7 @@ func (m *MockClient) CreateCheckout(ctx context.Context, req CreateCheckoutReq)
m.mu.Unlock()
go func() {
time.Sleep(3 * time.Second)
mockSleep(3 * time.Second)
m.mu.Lock()
defer m.mu.Unlock()
@@ -122,7 +130,7 @@ func (m *MockClient) CreateCheckout(ctx context.Context, req CreateCheckoutReq)
tipAmount = 500
amount += tipAmount
}
fees := amount*175/10000 // in-person rate: 1.75%
fees := amount * 175 / 10000 // in-person rate: 1.75%
paymentResult := &PaymentResult{
ID: paymentID,
@@ -171,7 +179,7 @@ func (m *MockClient) GetCheckout(ctx context.Context, checkoutID string) (*Payme
func (m *MockClient) RefundPayment(ctx context.Context, req RefundPaymentReq) (*RefundResult, error) {
log.Printf("[SQUARE-MOCK] RefundPayment: payment=%s, amount=%d", req.PaymentID, req.Amount)
time.Sleep(1 * time.Second)
mockSleep(1 * time.Second)
m.mu.Lock()
defer m.mu.Unlock()
@@ -285,4 +293,4 @@ func (m *MockClient) DeleteCardOnFile(ctx context.Context, cardID string) error
}
}
return fmt.Errorf("card not found: %s", cardID)
}
}
+1 -1
View File
@@ -350,4 +350,4 @@ func TestDevClient_ConcurrentPayments(t *testing.T) {
if resultCount != 10 {
t.Errorf("expected 10 results, got %d", resultCount)
}
}
}
+17 -17
View File
@@ -27,15 +27,15 @@ type RefundPaymentReq struct {
}
type PaymentResult struct {
ID string
Status string // "COMPLETED", "FAILED", "PENDING"
Amount int64
CardBrand string
CardLast4 string
TipAmount int64
ReceiptURL string
SquarePayID string // Square's payment ID
Fees int64 // processing fee in pence
ID string
Status string // "COMPLETED", "FAILED", "PENDING"
Amount int64
CardBrand string
CardLast4 string
TipAmount int64
ReceiptURL string
SquarePayID string // Square's payment ID
Fees int64 // processing fee in pence
}
type CheckoutResult struct {
@@ -44,14 +44,14 @@ type CheckoutResult struct {
}
type CardOnFile struct {
ID string
CardID string // Square's card-on-file token
Brand string
Last4 string
ExpMonth int
ExpYear int
ID string
CardID string // Square's card-on-file token
Brand string
Last4 string
ExpMonth int
ExpYear int
Fingerprint string
IsDefault bool
IsDefault bool
}
type RefundResult struct {
@@ -69,4 +69,4 @@ type SquareClient interface {
CreateCardOnFileRaw(ctx context.Context, userID, cardNumber string, expMonth, expYear int, cvc string) (*CardOnFile, error)
GetCardsOnFile(ctx context.Context, userID string) ([]CardOnFile, error)
DeleteCardOnFile(ctx context.Context, cardID string) error
}
}