Skip to main content

emv_3ds/message/
pres.rs

1use crate::types::{ActionIndicator, MessageVersion};
2use serde::{Deserialize, Serialize};
3
4/// EMV 3DS Preparation Response (PRes).
5///
6/// Returned by the Directory Server with card range data and supported protocol
7/// versions. The 3DS Server uses `cardRangeData` to look up the ACS URL and
8/// the protocol version range for a given PAN.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct PreparationResponse {
12    pub message_type: MessageType,
13    pub message_version: MessageVersion,
14    #[serde(rename = "threeDSServerTransID")]
15    pub three_ds_server_trans_id: String,
16    #[serde(rename = "dsTransID", skip_serializing_if = "Option::is_none")]
17    pub ds_trans_id: Option<String>,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub serial_num: Option<String>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub ds_reference_number: Option<String>,
22    pub ds_start_protocol_version: String,
23    pub ds_end_protocol_version: String,
24    #[serde(default, skip_serializing_if = "Vec::is_empty")]
25    pub card_range_data: Vec<CardRangeData>,
26}
27
28impl PreparationResponse {
29    /// Return the card range entry that covers `pan`, if any.
30    pub fn range_for_pan(&self, pan: &str) -> Option<&CardRangeData> {
31        self.card_range_data
32            .iter()
33            .find(|r| pan >= r.start_range.as_str() && pan <= r.end_range.as_str())
34    }
35}
36
37/// A single BIN-range entry returned in a PRes payload.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct CardRangeData {
41    pub start_range: String,
42    pub end_range: String,
43    pub action_ind: ActionIndicator,
44    pub acs_start_protocol_version: String,
45    pub acs_end_protocol_version: String,
46    #[serde(rename = "threeDSMethodURL", skip_serializing_if = "Option::is_none")]
47    pub three_ds_method_url: Option<String>,
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub ds_start_protocol_version: Option<String>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub ds_end_protocol_version: Option<String>,
52    #[serde(rename = "dsTransID", skip_serializing_if = "Option::is_none")]
53    pub ds_trans_id: Option<String>,
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub acs_info_ind: Option<Vec<String>>,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
59pub enum MessageType {
60    PRes,
61}