Round-A fresh review (6 agents) + fix + secondary cross-cutting + verification rounds: - F1: campaign discounts reduce the charged amount (deposit credit + admin PaymentModal discounted total); capDiscountToRemainingObligation prevents over-credit at completion in all four campaign blocks - F2: sweep replay rescue distinguishes legitimate same-key retries (21h window) from expired-key new charges; ccof blind-fails leave pending + CRITICAL instead of clawing back - F3: post-start online overflow carved as a tip record (mirrors terminal split builder) - A1: single-source Square decline-code classification (till delegates to square.IsDefinitivePaymentError) - A2/A5: refund attempt-cap literals consolidated; refund-failure counter capped + reset on terminal resolutions + admin notifications - A3/A9: idempotency helpers adopted across derivations; IsExplicitDevOrMockEnv relocated + all gates unified (incl. health-check) - A7: 2FA user+IP limiter + TRUST_PROXY_HEADERS startup warning; SNAPSHOT_ENC_KEY startup validation; TWO_FACTOR_PEPPER docs corrected - A8: snapshot encryption on all 6 write sites + marker-aware reuse paths; MPV->SPV effective voucher type (single VAT point) - A10/A11/A12/A16: gift-card slot scan advances past failed; amount-aware refund reconciliation; completed-booking refund re-check; PaymentWasRefunded on SquareClient interface - Dedup refund revalidation on tip/terminal/gift-card paths; sweep acknowledged_at IS NULL parity; refund-notification single source (exported payments.InsertRefundFailedNotifications) - Duplication/modularisation round: shared frontend helpers (sanitizeDecimalInput, campaignDiscountCents, twoFactorBlocksSavedCards getter, generateUUID), single-source MaxIdempotencyKeyLength, notification-helper consolidation, snapshot-guard comments - Cross-cutting GBP rename: Cents->Pence across backend + frontend + tests (26 identifiers, 16 files) - Tests: 11 behavior-change tests updated to new invariants; coverage for fixed functions; frontend vitest 55 tests; docs corrected (test counts, 2FA delivery, pre-launch checklist, resolution status) - gitleaks: allowlist backend/internal/square test fixtures (mock idempotency keys) All 25 backend packages pass; frontend 55/55 + build clean; env-docs 41/41.
86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
//go:build !dev
|
|
|
|
package square
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
var Client SquareClient
|
|
|
|
type ProdClient struct{}
|
|
|
|
func NewClient() SquareClient {
|
|
return NewProdClient()
|
|
}
|
|
|
|
func NewProdClient() SquareClient {
|
|
return &ProdClient{}
|
|
}
|
|
|
|
func (p *ProdClient) CreatePayment(ctx context.Context, req CreatePaymentReq) (*PaymentResult, error) {
|
|
return createPaymentHTTP(ctx, req)
|
|
}
|
|
|
|
func (p *ProdClient) CreateCheckout(ctx context.Context, req CreateCheckoutReq) (*CheckoutResult, error) {
|
|
return createCheckoutHTTP(ctx, req)
|
|
}
|
|
|
|
func (p *ProdClient) GetCheckout(ctx context.Context, checkoutID string) (*PaymentResult, error) {
|
|
return getCheckoutHTTP(ctx, checkoutID)
|
|
}
|
|
|
|
func (p *ProdClient) GetPayment(ctx context.Context, paymentID string) (*PaymentResult, error) {
|
|
return getPaymentHTTP(ctx, paymentID)
|
|
}
|
|
|
|
func (p *ProdClient) ReplayPaymentByKey(ctx context.Context, snapshotJSON []byte) (*PaymentResult, error) {
|
|
return replayPaymentByKeyHTTP(ctx, snapshotJSON)
|
|
}
|
|
|
|
func (p *ProdClient) CreateCustomer(ctx context.Context, name, email string) (*CustomerResult, error) {
|
|
return createCustomerHTTP(ctx, name, email)
|
|
}
|
|
|
|
func (p *ProdClient) DeleteCustomer(ctx context.Context, customerID string) error {
|
|
return deleteCustomerHTTP(ctx, customerID)
|
|
}
|
|
|
|
func (p *ProdClient) CancelCheckout(ctx context.Context, checkoutID string) error {
|
|
return cancelCheckoutHTTP(ctx, checkoutID)
|
|
}
|
|
|
|
func (p *ProdClient) RefundPayment(ctx context.Context, req RefundPaymentReq) (*RefundResult, error) {
|
|
return refundPaymentHTTP(ctx, req)
|
|
}
|
|
|
|
// PaymentWasRefunded has ZERO production callers (grep across the repo
|
|
// confirms the only users are this package's tests) and is kept on the
|
|
// SquareClient interface solely so the dev mock's refund-reconciliation
|
|
// parity tests can exercise the COMPLETED/APPROVED/PENDING status set.
|
|
// Production reconciliation uses the package-level
|
|
// paymentRefundedExactlyWithClient inside refundPaymentHTTP instead.
|
|
func (p *ProdClient) PaymentWasRefunded(ctx context.Context, paymentID string) (bool, error) {
|
|
return paymentWasRefundedWithClient(ctx, paymentID, newHTTPClient())
|
|
}
|
|
|
|
func (p *ProdClient) CreateCardOnFile(ctx context.Context, userID, cardToken, customerID string) (*CardOnFile, error) {
|
|
return createCardOnFileHTTP(ctx, userID, cardToken, customerID)
|
|
}
|
|
|
|
// GetCardsOnFile has ZERO production callers (grep across the repo confirms
|
|
// the only users are this package's tests) and is kept on the SquareClient
|
|
// interface solely so the dev mock's List Cards parity tests can exercise it.
|
|
func (p *ProdClient) GetCardsOnFile(ctx context.Context, userID string) ([]CardOnFile, error) {
|
|
return getCardsOnFileHTTP(ctx, userID)
|
|
}
|
|
|
|
func (p *ProdClient) DeleteCardOnFile(ctx context.Context, cardID string) error {
|
|
return deleteCardOnFileHTTP(ctx, cardID)
|
|
}
|
|
|
|
func (p *ProdClient) ListPaymentRefunds(ctx context.Context, paymentID string, beginTime time.Time) ([]RefundResult, error) {
|
|
return listRefundsHTTP(ctx, paymentID, beginTime)
|
|
}
|