Fix review findings: till idempotency keys, sha256 card key, paymentFromSquare fallback, regression test
till.go: - Replace deterministicTillKey (request-field hash) with uniqueTillKey (crypto/rand.Text) - Deterministic hashes broke legitimate identical create sales (empty gift_card_id collides on till_sales idempotency_key UNIQUE constraint → 500 on second sale) - Client-supplied keys handle dedup; fallback only needs uniqueness - Use rand.Text() (Go 1.24+) instead of deprecated rand.Read with dead error check till_test.go: - Add TestCreateTillSale_TwoIdenticalCreateSales_BothSucceed regression test (two identical keyless cash creates must both return 201) square_http_client.go: - createCardOnFileHTTP: replace reversible hex encoding with crypto/sha256 - paymentFromSquare: replace dead-code fallback with reachable else branch - Remove unused url import cleanup where applicable
This commit is contained in:
@@ -3,6 +3,7 @@ package square
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -343,12 +344,10 @@ func createCardOnFileHTTP(ctx context.Context, userID, cardToken string) (*CardO
|
||||
|
||||
// Deterministic idempotency key derived from user + card (not time-based)
|
||||
// so that retries with the same details don't create duplicate cards.
|
||||
ikHash := fmt.Sprintf("%x", []byte(userID+"|"+cardToken))
|
||||
if len(ikHash) > 64 {
|
||||
ikHash = ikHash[:64]
|
||||
}
|
||||
// SHA-256 hash prevents recovering the card token from the key itself.
|
||||
ikHash := sha256.Sum256([]byte(userID + "|" + cardToken))
|
||||
body := sqCreateCardRequest{
|
||||
IdempotencyKey: fmt.Sprintf("create-card-%s", ikHash),
|
||||
IdempotencyKey: fmt.Sprintf("create-card-%x", ikHash),
|
||||
SourceID: cardToken,
|
||||
Card: sqCardPayload{
|
||||
CustomerID: userID,
|
||||
@@ -422,14 +421,12 @@ func paymentFromSquare(sq *sqPayment) *PaymentResult {
|
||||
r.CardFingerprint = cd.Card.Fingerprint
|
||||
r.ExpMonth = cd.Card.ExpMonth
|
||||
r.ExpYear = cd.Card.ExpYear
|
||||
} else {
|
||||
// Card details present but no card ID — still surface the brand/last4.
|
||||
r.CardBrand = cd.Card.CardBrand
|
||||
r.CardLast4 = cd.Card.Last4
|
||||
}
|
||||
}
|
||||
if r.CardBrand == "" && sq.CardDetails != nil && sq.CardDetails.Card.ID != "" {
|
||||
r.CardBrand = sq.CardDetails.Card.CardBrand
|
||||
}
|
||||
if r.CardLast4 == "" && sq.CardDetails != nil && sq.CardDetails.Card.ID != "" {
|
||||
r.CardLast4 = sq.CardDetails.Card.Last4
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user