Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept bank cards only for specific connectors #997

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:
dorezyuk marked this conversation as resolved.
Show resolved Hide resolved
description: Indicates whether credit cards should be accepted for the connectors given with `credit_card_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>,
dorezyuk marked this conversation as resolved.
Show resolved Hide resolved

/// 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
dorezyuk marked this conversation as resolved.
Show resolved Hide resolved
} 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