PSR 2017 reg 100 makes SCA mandatory and non-waivable for customer-initiated stored-credential charges; a merchant-side 2FA check cannot legally substitute for it (authorising a token-less charge via 2FA leaves the MERCHANT liable for ECI 7 / SLI 210 chargebacks and reg 77(6) compensation regardless of consent). - payments/twofa.go: the homegrown 2FA fallback for token-less saved-card charges is REMOVED ENTIRELY. requireTwoFactorForCardAccess is now SCA-only: a non-empty Square verification_token (charge surfaces, token forwarded to Square) skips the gate; anything else is refused 402 verification_required. enforceSCAFallbackConsent is a compile-compatible no-op (fallback never runs). - New requireTwoFactorForCardAccessWithTokenValidation distinguishes surfaces where the token IS forwarded to Square (charge — Square validates it) from card-SAVE surfaces (token client-asserted, never forwarded: a non-empty token must NOT skip the save gate, auth-F1). - SCA tokenize-result wire contract (C1): a saved card charged with a fresh one-time tokenize-result sends the token as the charge SOURCE (new_card_token -> source_id) alongside saved_card_id, never a separate verification_token. resolveChargeSource resolves the saved-card branch FIRST (customer from the card row, token as source) so combined token+card requests are SCA-clean. - C6 consent fields (consent_version / consent_accepted) added to the booking/ tip/till/gift-card charge requests, enforced server-side before any fallback charge could reach Square and recorded on the 2fa_fallback_charge audit row; logVerificationTokenProvenance traces minted tokens to their charge. - user 2FA issuance gate refactored into pure build-agnostic functions (twoFAPepperConfigured / twoFADeliveryChannelConfigured / twoFAEnsureIssueAllowedStrict) shared with the payments re-issue path and exercised directly by the test,dev suite; TWO_FACTOR_FALLBACK switch and .env.example entry removed; startup posture notes updated. - Test coverage: fail-closed 2FA production gates (pepper/delivery), token validation on save vs charge surfaces, completion idempotency, idempotency key determinism, refund-policy 72h/24h epsilon boundaries, VAT parity.
36 lines
1.8 KiB
Go
36 lines
1.8 KiB
Go
//go:build dev || test
|
|
|
|
package payments
|
|
|
|
import "log"
|
|
|
|
// twoFADeliveryAvailable reports whether a 2FA code delivery channel exists in
|
|
// this build. Dev/test builds always have one — the [2FA] log line is the
|
|
// documented loose-fake delivery channel — so the 2FA BACKUP authorization
|
|
// (the saved-card gate when SCA is unavailable) is always usable here. Mirrors
|
|
// handlers/user/twofa_dev.go; production builds decide in
|
|
// twofa_delivery_prod.go.
|
|
func twoFADeliveryAvailable() bool { return true }
|
|
|
|
// twoFAReissueIssueAllowed is the re-issue path's issuance gate
|
|
// (reissueTwoFACodeAfterFailedCharge, handlers.go), mirroring the user
|
|
// package's twoFAEnsureIssueAllowed build-tagged semantics: dev/test builds
|
|
// always allow issuance — the [2FA] log line is the delivery channel and the
|
|
// unsalted-digest fallback is the documented loose-fake stand-in (matching
|
|
// twofa_dev.go). Production builds fail closed here — no pepper, no delivery
|
|
// channel, no codes (see twofa_delivery_prod.go).
|
|
func twoFAReissueIssueAllowed() error { return nil }
|
|
|
|
// twoFAReissueDeliverCode delivers a re-issued code (a fresh saved-card charge
|
|
// consumed the customer's code at the gate and the charge failed at Square).
|
|
// Dev/test builds always deliver via the [2FA] log line — the documented
|
|
// loose-fake delivery channel — so the operator can relay the fresh code to the
|
|
// customer. Mirrors the user package's twoFADeliverCode; production logs it
|
|
// ONLY with the explicit TWO_FACTOR_ALLOW_LOG_DELIVERY opt-in (see
|
|
// twofa_delivery_prod.go). MEDIUM-3b: the user id and the plaintext code go to
|
|
// SEPARATE log lines so a single record cannot trivially pair them.
|
|
func twoFAReissueDeliverCode(userID, code string) {
|
|
log.Printf("[2FA] code delivery requested (user=%s, purpose=re-issue after failed saved-card charge)", userID)
|
|
log.Printf("[2FA] code: %s", code)
|
|
}
|