Skip to main content

emv_3ds/types/
coded.rs

1use serde::{Deserialize, Serialize};
2
3/// EMV 3DS specification version.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub enum MessageVersion {
6    #[serde(rename = "2.1.0")]
7    V210,
8    #[serde(rename = "2.2.0")]
9    V220,
10    #[serde(rename = "2.3.0")]
11    V230,
12}
13
14impl MessageVersion {
15    pub fn as_str(self) -> &'static str {
16        match self {
17            Self::V210 => "2.1.0",
18            Self::V220 => "2.2.0",
19            Self::V230 => "2.3.0",
20        }
21    }
22}
23
24impl std::fmt::Display for MessageVersion {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        f.write_str(self.as_str())
27    }
28}
29
30/// Device channel through which the transaction originates.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub enum DeviceChannel {
33    /// 01 — App-based (native SDK)
34    #[serde(rename = "01")]
35    App,
36    /// 02 — Browser
37    #[serde(rename = "02")]
38    Browser,
39    /// 03 — 3DS Requestor Initiated (3RI)
40    #[serde(rename = "03")]
41    ThreeDsRequestorInitiated,
42}
43
44/// Category of authentication being requested.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
46pub enum MessageCategory {
47    /// 01 — Payment Authentication
48    #[serde(rename = "01")]
49    PaymentAuthentication,
50    /// 02 — Non-Payment Authentication
51    #[serde(rename = "02")]
52    NonPaymentAuthentication,
53}
54
55/// Outcome of the authentication transaction.
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
57pub enum TransStatus {
58    /// Y — Authentication / Account Verification Successful
59    #[serde(rename = "Y")]
60    Success,
61    /// N — Not Authenticated / Account Not Verified
62    #[serde(rename = "N")]
63    Failure,
64    /// U — Authentication / Account Verification Could Not Be Performed
65    #[serde(rename = "U")]
66    Unable,
67    /// A — Attempts Processing Performed
68    #[serde(rename = "A")]
69    Attempted,
70    /// C — Challenge Required; additional authentication needed
71    #[serde(rename = "C")]
72    ChallengeRequired,
73    /// D — Decoupled Authentication Confirmed
74    #[serde(rename = "D")]
75    DecoupledRequired,
76    /// I — Informational Only; 3DS Requestor challenge preference acknowledged
77    #[serde(rename = "I")]
78    InformationalOnly,
79    /// R — Authentication / Account Verification Rejected
80    #[serde(rename = "R")]
81    Rejected,
82}
83
84impl TransStatus {
85    /// True if the authentication was successful (frictionless Y or attempted A).
86    pub fn is_authenticated(self) -> bool {
87        matches!(self, Self::Success | Self::Attempted)
88    }
89
90    /// True if a challenge must be presented to the cardholder.
91    pub fn requires_challenge(self) -> bool {
92        matches!(self, Self::ChallengeRequired | Self::DecoupledRequired)
93    }
94}
95
96/// Reason code accompanying a non-successful TransStatus.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
98pub enum TransStatusReason {
99    #[serde(rename = "01")]
100    CardAuthenticationFailed,
101    #[serde(rename = "02")]
102    UnknownDevice,
103    #[serde(rename = "03")]
104    UnsupportedDevice,
105    #[serde(rename = "04")]
106    ExceedsAuthenticationFrequencyLimit,
107    #[serde(rename = "05")]
108    ExpiredCard,
109    #[serde(rename = "06")]
110    InvalidCardNumber,
111    #[serde(rename = "07")]
112    InvalidTransaction,
113    #[serde(rename = "08")]
114    NoCardRecord,
115    #[serde(rename = "09")]
116    SecurityFailure,
117    #[serde(rename = "10")]
118    StolenCard,
119    #[serde(rename = "11")]
120    SuspectedFraud,
121    #[serde(rename = "12")]
122    TransactionNotPermittedToCardholder,
123    #[serde(rename = "13")]
124    CardholderNotEnrolledInService,
125    #[serde(rename = "14")]
126    TransactionTimedOutAtAcs,
127    #[serde(rename = "15")]
128    LowConfidence,
129    #[serde(rename = "16")]
130    MediumConfidence,
131    #[serde(rename = "17")]
132    HighConfidence,
133    #[serde(rename = "18")]
134    VeryHighConfidence,
135    #[serde(rename = "19")]
136    ExceedsMaxChallengesPerTransaction,
137    #[serde(rename = "20")]
138    NonPaymentNotSupported,
139    #[serde(rename = "21")]
140    ThreeDsRequestorChallengeIndicator3NotAccepted,
141}
142
143/// Electronic Commerce Indicator — liability shift outcome.
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
145pub enum Eci {
146    /// Visa: fully authenticated
147    #[serde(rename = "05")]
148    VisaFullyAuthenticated,
149    /// Visa: attempted
150    #[serde(rename = "06")]
151    VisaAttempted,
152    /// Visa: not authenticated / no liability shift
153    #[serde(rename = "07")]
154    VisaNotAuthenticated,
155    /// Mastercard: fully authenticated
156    #[serde(rename = "02")]
157    MastercardFullyAuthenticated,
158    /// Mastercard: attempted
159    #[serde(rename = "01")]
160    MastercardAttempted,
161    /// Mastercard: not authenticated
162    #[serde(rename = "00")]
163    MastercardNotAuthenticated,
164}
165
166impl Eci {
167    /// True if this ECI value represents a successful liability shift.
168    pub fn has_liability_shift(self) -> bool {
169        matches!(
170            self,
171            Self::VisaFullyAuthenticated
172                | Self::VisaAttempted
173                | Self::MastercardFullyAuthenticated
174                | Self::MastercardAttempted
175        )
176    }
177}
178
179/// Requestor's preference for whether a challenge should be performed.
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
181pub enum ChallengeIndicator {
182    /// 01 — No preference
183    #[serde(rename = "01")]
184    NoPreference,
185    /// 02 — No challenge requested
186    #[serde(rename = "02")]
187    NoChallengeRequested,
188    /// 03 — Challenge requested (requestor preference)
189    #[serde(rename = "03")]
190    ChallengeRequested,
191    /// 04 — Challenge requested (mandate)
192    #[serde(rename = "04")]
193    ChallengeRequestedMandate,
194    /// 05 — No challenge (transactional risk analysis already performed)
195    #[serde(rename = "05")]
196    NoChallengeRiskAnalysisPerformed,
197    /// 06 — No challenge (Data Share Only)
198    #[serde(rename = "06")]
199    NoChallengeDataShareOnly,
200    /// 07 — No challenge (SCA already performed)
201    #[serde(rename = "07")]
202    NoChallengeSCAAlreadyPerformed,
203    /// 08 — No challenge (whitelisted)
204    #[serde(rename = "08")]
205    NoChallengeWhitelisted,
206    /// 09 — Challenge requested (whitelisting prompt requested)
207    #[serde(rename = "09")]
208    ChallengeRequestedWhitelistPrompt,
209}
210
211/// Dimensions of the challenge window presented in the browser.
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
213pub enum ChallengeWindowSize {
214    #[serde(rename = "01")]
215    W250x400,
216    #[serde(rename = "02")]
217    W390x400,
218    #[serde(rename = "03")]
219    W500x600,
220    #[serde(rename = "04")]
221    W600x400,
222    #[serde(rename = "05")]
223    FullScreen,
224}
225
226/// How the 3DS Method was used to collect device fingerprint data.
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
228pub enum ThreeDsMethod {
229    /// 00 — 3DS Method Not Available / Requestor chose not to use
230    #[serde(rename = "00")]
231    NotAvailableOrNotUsed,
232    /// 01 — 3DS Method URL was present and successfully invoked
233    #[serde(rename = "01")]
234    Successful,
235    /// 02 — 3DS Method URL was present but failed or timed out
236    #[serde(rename = "02")]
237    Failed,
238}
239
240/// How the cardholder authenticates to the 3DS Requestor.
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
242pub enum AuthenticationMethod {
243    #[serde(rename = "01")]
244    NoThreeDsRequestorAuthentication,
245    #[serde(rename = "02")]
246    LoginToRequestorAccount,
247    #[serde(rename = "03")]
248    FederatedId,
249    #[serde(rename = "04")]
250    IssuerCredentials,
251    #[serde(rename = "05")]
252    ThirdPartyAuthentication,
253    #[serde(rename = "06")]
254    FidoAuthenticator,
255}
256
257#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
258pub enum DeliveryTimeframe {
259    #[serde(rename = "01")]
260    ElectronicDelivery,
261    #[serde(rename = "02")]
262    SameDayShipping,
263    #[serde(rename = "03")]
264    OvernightShipping,
265    #[serde(rename = "04")]
266    TwoDayOrMoreShipping,
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
270pub enum ShipIndicator {
271    #[serde(rename = "01")]
272    ShipToCardholdersBillingAddress,
273    #[serde(rename = "02")]
274    ShipToAnotherVerifiedAddress,
275    #[serde(rename = "03")]
276    ShipToDifferentAddress,
277    #[serde(rename = "04")]
278    ShipToStore,
279    #[serde(rename = "05")]
280    DigitalGoods,
281    #[serde(rename = "06")]
282    NotShipped,
283    #[serde(rename = "07")]
284    Other,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
288pub enum ReorderItemsIndicator {
289    #[serde(rename = "01")]
290    FirstTimeOrdered,
291    #[serde(rename = "02")]
292    Reordered,
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
296pub enum PreOrderPurchaseIndicator {
297    #[serde(rename = "01")]
298    MerchandiseAvailable,
299    #[serde(rename = "02")]
300    FutureAvailability,
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
304pub enum SuspiciousAccActivity {
305    #[serde(rename = "01")]
306    NoSuspiciousActivity,
307    #[serde(rename = "02")]
308    SuspiciousActivityObserved,
309}
310
311/// Card range update action in a PRes payload.
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
313pub enum ActionIndicator {
314    #[serde(rename = "A")]
315    Add,
316    #[serde(rename = "M")]
317    Modify,
318    #[serde(rename = "D")]
319    Delete,
320}
321
322/// Authentication method used by the ACS (reported in RReq).
323#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
324pub enum AcsAuthMethod {
325    #[serde(rename = "01")]
326    NoAuthentication,
327    #[serde(rename = "02")]
328    SmsOtp,
329    #[serde(rename = "03")]
330    KnowledgeBased,
331    #[serde(rename = "04")]
332    Oob,
333    #[serde(rename = "05")]
334    Cvc2,
335    #[serde(rename = "06")]
336    ThreeDsServerProprietary,
337    #[serde(rename = "07")]
338    RiskBased,
339    #[serde(rename = "08")]
340    DigitalToken,
341    #[serde(rename = "09")]
342    PushNotification,
343    #[serde(rename = "10")]
344    Biometric,
345    #[serde(rename = "11")]
346    FidoAssertion,
347    #[serde(rename = "12")]
348    AppBased,
349}
350
351/// Type of authentication performed by the ACS (reported in RReq).
352#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
353pub enum AuthenticationType {
354    #[serde(rename = "01")]
355    Frictionless,
356    #[serde(rename = "02")]
357    ChallengeOtp,
358    #[serde(rename = "03")]
359    ChallengeOob,
360    #[serde(rename = "04")]
361    ChallengeRba,
362    #[serde(rename = "05")]
363    ChallengeAcsDecision,
364}
365
366/// Acknowledgment status in RRes.
367#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
368pub enum ResultsStatus {
369    /// Request received and all required data present.
370    #[serde(rename = "01")]
371    Received,
372    /// Out-of-sync data received; requestor should retry.
373    #[serde(rename = "02")]
374    OutOfSync,
375}