Harden Square HTTP client and dev mock: DeleteCustomer, response limits, token redaction
Adds SquareClient.DeleteCustomer for GDPR erasure (DELETE /v2/customers/{id}, NOT_FOUND as no-op), bounds doJSON response reads to 1 MiB with rune-safe 500-byte error snippets, adds validCardID guard to the disable-card URL, makes paymentFromSquare card fields consistent when the card ID is empty, redacts ccof/cnon tokens in all log paths, and fixes the idempotency-key-length comment (45 chars for payments/refunds/cards, 64 only for terminal checkouts).
This commit is contained in:
@@ -69,6 +69,10 @@ func (d *devProdClient) GetPayment(ctx context.Context, paymentID string) (*Paym
|
||||
func (d *devProdClient) CreateCustomer(ctx context.Context, name, email string) (*CustomerResult, error) {
|
||||
return createCustomerHTTP(ctx, name, email)
|
||||
}
|
||||
|
||||
func (d *devProdClient) DeleteCustomer(ctx context.Context, customerID string) error {
|
||||
return deleteCustomerHTTP(ctx, customerID)
|
||||
}
|
||||
func (d *devProdClient) CancelCheckout(ctx context.Context, checkoutID string) error {
|
||||
return cancelCheckoutHTTP(ctx, checkoutID)
|
||||
}
|
||||
@@ -566,7 +570,7 @@ func (m *MockClient) CreateCustomer(ctx context.Context, name, email string) (*C
|
||||
// the mock mirrors this by deduping on email so a retry returns the
|
||||
// original customer rather than creating a duplicate.
|
||||
if existing, ok := m.customers[email]; ok {
|
||||
log.Printf("[SQUARE-MOCK] CreateCustomer dedup hit: email=%s → id=%s", redactedEmail(email), existing.ID)
|
||||
log.Printf("[SQUARE-MOCK] CreateCustomer dedup hit: email=%s → id=%s", redactedEmail(email), tokenPrefix(existing.ID))
|
||||
return existing, nil
|
||||
}
|
||||
|
||||
@@ -577,10 +581,28 @@ func (m *MockClient) CreateCustomer(ctx context.Context, name, email string) (*C
|
||||
CreatedAt: clock.Now().UTC().Format(time.RFC3339),
|
||||
}
|
||||
m.customers[email] = customer
|
||||
log.Printf("[SQUARE-MOCK] Customer created: id=%s, email=%s", customer.ID, redactedEmail(email))
|
||||
log.Printf("[SQUARE-MOCK] Customer created: id=%s, email=%s", tokenPrefix(customer.ID), redactedEmail(email))
|
||||
return customer, nil
|
||||
}
|
||||
|
||||
func (m *MockClient) DeleteCustomer(ctx context.Context, customerID string) error {
|
||||
log.Printf("[SQUARE-MOCK] DeleteCustomer: id=%s", tokenPrefix(customerID))
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for email, customer := range m.customers {
|
||||
if customer.ID == customerID {
|
||||
delete(m.customers, email)
|
||||
log.Printf("[SQUARE-MOCK] Customer deleted: id=%s", tokenPrefix(customerID))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// Real Square returns 404 / NOT_FOUND for an already-deleted customer —
|
||||
// mirror the prod semantics of idempotent re-deletion as a no-op.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockClient) CancelCheckout(ctx context.Context, checkoutID string) error {
|
||||
log.Printf("[SQUARE-MOCK] CancelCheckout: id=%s", checkoutID)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user