open-standards/openpay/crates/op-3ds2/src/error.rs
Transaction Science 3fe109d58f ingest batch 4: 9 crates · table-stakes completers across all three workspaces
OpenPay (3 crates, ~13k LOC, 175 tests)
  op-connect           sub-merchant onboarding + KYB + split-payments + payouts + 1099-K
  op-3ds2              3DS2.3 SCA processor (AReq/ARes/CReq/CRes/RReq/RRes + exemptions)
  op-tax               US/EU/UK/AU/CA/IN tax with Avalara/Stripe/CCH/Vertex/TaxJar adapters

Smart Byte (3 crates, ~7k LOC, 57 tests)
  smart-byte-libp2p    libp2p transport (kad/gossipsub/mdns/autonat/relay/dcutr/identify/ping/req_resp)
  smart-byte-crdt      LWW/GCounter/PnCounter/OrSet/LwwMap/RgaList/2P-Set + HLC + vector clock + Automerge/Yjs interop
  smart-byte-keri-witness  KERI key-rotation cycle + witness layer + watchers + recovery + delegation

EOC (3 crates, ~8k LOC, 108 tests)
  eoc-tools            function-calling across Anthropic/OpenAI/Google/Mistral/Cohere + parallel exec + sandbox
  eoc-multimodal       vision (CLIP/SigLIP/ColPali) + audio (Whisper/MMS) + video sampling + modality router
  eoc-spec-decode      vanilla + SpS-with-temp + lookahead speculative decoding + EAGLE/Medusa stubs

All three workspaces compile cleanly. Forbidden-unsafe, no unwrap outside tests.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-24 04:00:04 -04:00

102 lines
3.5 KiB
Rust

//! Typed errors for `op-3ds2`.
//!
//! One sealed enum. Every public fallible API returns `Result<T>` from
//! this module so callers can `match` exhaustively. Adding a variant is
//! a SemVer-major change.
use thiserror::Error;
/// Result alias.
pub type Result<T> = core::result::Result<T, Error>;
/// All possible failure modes inside `op-3ds2`.
#[derive(Debug, Error)]
pub enum Error {
/// JSON encode / decode of a 3DS message failed.
#[error("json codec error: {0}")]
Json(#[from] serde_json::Error),
/// XML encode / decode (legacy 2.1.0 transition payloads).
#[error("xml codec error: {0}")]
Xml(String),
/// A required field for the selected protocol version was missing.
#[error("required field missing for {version:?}: {field}")]
MissingField {
/// Spec version we were emitting for.
version: crate::version::ProtocolVersion,
/// Field name as published in the EMVCo message catalogue.
field: &'static str,
},
/// A field that the selected version forbids was present.
#[error("field {field} is not permitted in {version:?}")]
ForbiddenField {
/// Spec version we were validating against.
version: crate::version::ProtocolVersion,
/// Field name.
field: &'static str,
},
/// PAN failed the LUHN-10 check on the way into a version-check.
#[error("invalid PAN: failed LUHN check")]
InvalidPan,
/// PAN's BIN did not resolve to any scheme we route to.
#[error("no directory server route for PAN BIN {bin}")]
NoDsRoute {
/// First six (or eight) digits, safe to log.
bin: String,
},
/// The DS returned a transport-level error or unexpected status.
#[error("directory server transport error: {0}")]
DsTransport(String),
/// The ACS returned a transport-level error or unexpected status.
#[error("acs transport error: {0}")]
AcsTransport(String),
/// The ACS returned an [`ErrorMessage`](crate::message::ErrorMessage).
#[error("3ds error message: code={code} component={component}")]
ProtocolError {
/// EMVCo error code (5 digits).
code: String,
/// The component that originated the error.
component: String,
},
/// The challenge window exceeded the timeout (default 5 minutes).
#[error("challenge timeout exceeded")]
ChallengeTimeout,
/// Decoupled-authentication polling exceeded its budget.
#[error("decoupled authentication timed out after {polls} polls")]
DecoupledTimeout {
/// How many polls were issued before giving up.
polls: u32,
},
/// Browser fingerprint payload was malformed.
#[error("invalid fingerprint payload: {0}")]
InvalidFingerprint(&'static str),
/// A base64 / cryptogram decode failed.
#[error("invalid cryptogram encoding")]
InvalidCryptogram,
/// Version negotiation produced no overlap with the DS's supported
/// versions. Caller should fall back to a non-3DS authorization.
#[error("no common 3DS protocol version between requestor and DS")]
NoCommonVersion,
/// Exemption was requested but the runtime context did not satisfy
/// the regulatory prerequisites (e.g. TRA without a qualifying
/// fraud-rate bracket).
#[error("exemption {0} not eligible under PSD2/PSD3 RTS")]
ExemptionIneligible(&'static str),
/// An internal invariant was violated. Indicates a bug.
#[error("invariant violation: {0}")]
Invariant(&'static str),
}