Skip to content

Commit

Permalink
Accept bank cards only for specific connectors
Browse files Browse the repository at this point in the history
Signed-off-by: Wojciech Kula <[email protected]>
  • Loading branch information
wku12 committed Dec 20, 2024
1 parent 6e669fd commit 4fec003
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 25 deletions.
12 changes: 12 additions & 0 deletions modules/RsPaymentTerminal/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ config:
password:
description: Password for the Feig terminal.
type: integer
accept_credit_cards:
description: Indicates whether credit cards should be accepted for all connectors
type: boolean
default: true
credit_card_connectors:
description: >-
If the `accept_credit_cards` is set to true credit cards
will only provide authentication to the given connectors.
If list is empty, authentication will happen to all the connectors.
type: string
default: ""

requires:
session:
interface: session_cost
Expand Down
235 changes: 210 additions & 25 deletions modules/RsPaymentTerminal/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,28 @@ mod sync_feig {
#[mockall_double::double]
use sync_feig::SyncFeig;

fn string_to_vec(s: &String) -> Vec<i64> {
s.split(',')
.filter_map(|s| s.trim().parse::<i64>().ok())
.collect()
}

impl ProvidedIdToken {
fn new(id_token: String, authorization_type: AuthorizationType) -> Self {
fn new(
id_token: String,
authorization_type: AuthorizationType,
connectors: Option<Vec<i64>>,
) -> Self {
Self {
parent_id_token: None,
id_token: IdToken {
value: id_token,
r#type: IdTokenType::Local,
additional_info: None
additional_info: None,
},
authorization_type,
certificate: None,
connectors: None,
connectors: connectors,
iso_15118_certificate_hash_data: None,
prevalidated: None,
request_id: None,
Expand All @@ -154,6 +164,12 @@ pub struct PaymentTerminalModule {

/// The Feig interface.
feig: SyncFeig,

/// For which connectors credit cards should be accepted
credit_cards_connectors: Vec<i64>,

/// If credit cards should be accepted
accept_credit_cards: bool,
}

impl PaymentTerminalModule {
Expand Down Expand Up @@ -188,26 +204,43 @@ impl PaymentTerminalModule {
};
let card_info = read_card_loop()?;

let provided_token = match card_info {
if let Some(provided_token) = match card_info {
CardInfo::Bank => {
self.feig.begin_transaction(&token)?;

// Reuse the bank token as invoice token so we can use the
// invoice token later on to commit our transactions.
ProvidedIdToken::new(token, AuthorizationType::BankCard)
}
CardInfo::MembershipCard(id_token) => {
ProvidedIdToken::new(id_token, AuthorizationType::RFID)
if !self.accept_credit_cards {
None
} else {
self.feig.begin_transaction(&token)?;
let credit_cards_connectors = if self.credit_cards_connectors.is_empty() {
None
} else {
Some(self.credit_cards_connectors.clone())
};
// Reuse the bank token as invoice token so we can use the
// invoice token later on to commit our transactions.
Some(ProvidedIdToken::new(
token,
AuthorizationType::BankCard,
credit_cards_connectors,
))
}
}
};
publishers.token_provider.provided_token(provided_token)?;
CardInfo::MembershipCard(id_token) => Some(ProvidedIdToken::new(
id_token,
AuthorizationType::RFID,
None,
)),
} {
publishers.token_provider.provided_token(provided_token)?;
}
Ok(())
}

/// The implementation of the `SessionCostClientSubscriber::on_session_cost`,
/// but here we can return errors.
fn on_session_cost_impl(&self, context: &Context, value: SessionCost) -> Result<()> {
let Some(id_tag) = value.id_tag else { return Ok(()) };
let Some(id_tag) = value.id_tag else {
return Ok(());
};

// We only care about bank cards.
match id_tag.authorization_type {
Expand Down Expand Up @@ -292,6 +325,8 @@ fn main() -> Result<()> {
let pt_module = Arc::new(PaymentTerminalModule {
tx,
feig: SyncFeig::new(pt_config),
credit_cards_connectors: string_to_vec(&config.credit_card_connectors),
accept_credit_cards: config.accept_credit_cards,
});

let _module = Module::new(
Expand Down Expand Up @@ -347,7 +382,12 @@ mod tests {
let feig = SyncFeig::default();
let (tx, _) = channel();

let pt_module = PaymentTerminalModule { tx, feig };
let pt_module = PaymentTerminalModule {
tx,
feig,
credit_cards_connectors: vec![],
accept_credit_cards: true,
};

assert!(pt_module.begin_transaction(&everest_mock).is_err());
}
Expand Down Expand Up @@ -401,6 +441,113 @@ mod tests {
let pt_module = PaymentTerminalModule {
tx,
feig: feig_mock,
credit_cards_connectors: vec![],
accept_credit_cards: true,
};

assert!(pt_module.begin_transaction(&everest_mock).is_ok());
}
}
#[test]
/// Unit tests for the `PaymentTerminalModule::begin_transaction` with credit card acceptance.
fn payment_terminal_module__begin_transaction_credit_cards_accepted() {
// Now test the successful execution.
let parameters = [
(
CardInfo::Bank,
"my bank token",
true,
true,
String::from(""),
None,
),
(
CardInfo::Bank,
"my bank token",
false,
false,
String::from("1,2"),
None,
),
(
CardInfo::Bank,
"my bank token",
true,
true,
String::from("1"),
Some(vec![1]),
),
(
CardInfo::Bank,
"my bank token",
true,
true,
String::from("1,2"),
Some(vec![1, 2]),
),
(
CardInfo::Bank,
"my bank token",
true,
true,
String::from(""),
None,
),
];

for (
card_info,
expected_token,
expected_transaction,
accept_credit_cards,
credit_card_connectors,
expected_connectors,
) in parameters
{
let mut everest_mock = ModulePublisher::default();
let mut feig_mock = SyncFeig::default();

everest_mock
.bank_session_token
.expect_get_bank_session_token()
.times(1)
.return_once(|| {
Ok(BankSessionToken {
token: Some("my bank token".to_string()),
})
});
if expected_transaction {
everest_mock
.token_provider
.expect_provided_token()
.times(1)
.withf(move |arg| {
arg.id_token.value == expected_token.to_string()
&& arg.connectors == expected_connectors
})
.return_once(|_| Ok(()));
}

feig_mock
.expect_read_card()
.times(1)
.return_once(|| Ok(card_info));

if expected_transaction {
feig_mock
.expect_begin_transaction()
.times(1)
.with(eq("my bank token"))
.return_once(|_| Ok(()));
}

let (tx, _) = channel();

let pt_module = PaymentTerminalModule {
tx,
feig: feig_mock,
credit_cards_connectors: string_to_vec(&credit_card_connectors),
accept_credit_cards,
};

assert!(pt_module.begin_transaction(&everest_mock).is_ok());
Expand All @@ -417,7 +564,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new(String::new(), AuthorizationType::OCPP)),
id_tag: Some(ProvidedIdToken::new(
String::new(),
AuthorizationType::OCPP,
None,
)),
status: SessionStatus::Finished,
session_id: String::new(),
idle_price: None,
Expand All @@ -432,7 +583,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new(String::new(), AuthorizationType::RFID)),
id_tag: Some(ProvidedIdToken::new(
String::new(),
AuthorizationType::RFID,
None,
)),
status: SessionStatus::Finished,
session_id: String::new(),
idle_price: None,
Expand All @@ -447,7 +602,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new(String::new(), AuthorizationType::BankCard)),
id_tag: Some(ProvidedIdToken::new(
String::new(),
AuthorizationType::BankCard,
None,
)),
status: SessionStatus::Running,
session_id: String::new(),
idle_price: None,
Expand All @@ -468,7 +627,12 @@ mod tests {
let feig = SyncFeig::default();
let (tx, _) = channel();

let pt_module = PaymentTerminalModule { tx, feig };
let pt_module = PaymentTerminalModule {
tx,
feig,
credit_cards_connectors: vec![],
accept_credit_cards: true,
};
assert!(pt_module
.on_session_cost_impl(&context, session_cost)
.is_ok());
Expand All @@ -487,7 +651,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new("token".to_string(), AuthorizationType::BankCard)),
id_tag: Some(ProvidedIdToken::new(
"token".to_string(),
AuthorizationType::BankCard,
None,
)),
status: SessionStatus::Finished,
session_id: String::new(),
idle_price: None,
Expand All @@ -505,7 +673,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new("token".to_string(), AuthorizationType::BankCard)),
id_tag: Some(ProvidedIdToken::new(
"token".to_string(),
AuthorizationType::BankCard,
None,
)),
status: SessionStatus::Finished,
session_id: String::new(),
idle_price: None,
Expand All @@ -530,7 +702,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new("token".to_string(), AuthorizationType::BankCard)),
id_tag: Some(ProvidedIdToken::new(
"token".to_string(),
AuthorizationType::BankCard,
None,
)),
status: SessionStatus::Finished,
session_id: String::new(),
idle_price: None,
Expand Down Expand Up @@ -565,7 +741,11 @@ mod tests {
code: Some(CurrencyCode::EUR),
decimals: None,
},
id_tag: Some(ProvidedIdToken::new("token".to_string(), AuthorizationType::BankCard)),
id_tag: Some(ProvidedIdToken::new(
"token".to_string(),
AuthorizationType::BankCard,
None,
)),
status: SessionStatus::Finished,
session_id: String::new(),
idle_price: None,
Expand Down Expand Up @@ -606,7 +786,12 @@ mod tests {
});
let (tx, _) = channel();

let pt_module = PaymentTerminalModule { tx, feig };
let pt_module = PaymentTerminalModule {
tx,
feig,
credit_cards_connectors: vec![],
accept_credit_cards: true,
};
assert!(pt_module
.on_session_cost_impl(&context, session_cost)
.is_ok());
Expand Down

0 comments on commit 4fec003

Please sign in to comment.