-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add feig payment terminal code (#15)
* Add feig payment terminal code * fix test * fix * update metadat * change dep * Add README --------- Co-authored-by: Dima Dorezyuk <[email protected]>
- Loading branch information
Showing
10 changed files
with
2,132 additions
and
18 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ members = [ | |
"zvt_builder", | ||
"zvt_cli", | ||
"zvt_derive", | ||
"zvt_feig_terminal", | ||
] | ||
|
||
[workspace.package] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test") | ||
load("@crate_index//:defs.bzl", "all_crate_deps") | ||
|
||
rust_library( | ||
name = "zvt_feig_terminal", | ||
srcs = glob(["src/*.rs"]), | ||
deps = all_crate_deps() + ["//zvt"], | ||
proc_macro_deps = all_crate_deps(proc_macro = True), | ||
edition = "2021", | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
rust_test( | ||
name = "zvt_feig_terminal_test", | ||
deps = all_crate_deps(normal_dev = True), | ||
crate = ":zvt_feig_terminal", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[package] | ||
name = "zvt_feig_terminal" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
zvt = { version = "0.1.0", path = "../zvt" } | ||
anyhow = "1.0.75" | ||
tokio = { version = "1.32.0", features = ["macros", "rt-multi-thread", "net", "sync"] } | ||
log = "0.4.20" | ||
trust-dns-client = "0.23.0" | ||
trust-dns-proto = "0.23.0" | ||
async-trait = "0.1.73" | ||
env_logger = "0.10.0" | ||
tonic = "0.8.3" | ||
tokio-stream = "0.1.14" | ||
serde = { version = "1.0.188", features = ["derive"] } | ||
thiserror = "1.0.48" | ||
time = { version = "0.3.28", features = ["macros"] } | ||
time-macros = "0.2.14" | ||
pin-project = "1.1.3" | ||
futures = "0.3.28" | ||
async-stream = "0.3.5" | ||
mockall = "0.11.4" | ||
mockall_double = "0.3.0" | ||
num-traits = "0.2.17" | ||
|
||
[dev-dependencies] | ||
serde_json = "1.0.105" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# ZVT Feig Terminal | ||
|
||
The crate implements the application logic for using a Feig terminal in production. | ||
|
||
We assume that you interact with the Feig terminal over TCP/IP. | ||
|
||
The high level interface for interacting with the Feig terminal is implemented | ||
in [src/feig.rs](src/feig.rs). It provides good defaults for reading cards and | ||
allows you to begin, commit or cancel transactions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
use anyhow::{bail, Result}; | ||
use serde::Deserialize; | ||
use std::net::Ipv4Addr; | ||
|
||
/// The config for the Feig terminal included in the | ||
/// [PoleConfiguration::configuration]. | ||
#[derive(serde::Deserialize, PartialEq, Debug, Clone)] | ||
pub struct FeigConfig { | ||
/// The currency code as defined by ISO 4217. See | ||
/// https://en.wikipedia.org/wiki/ISO_4217. | ||
/// | ||
/// The input is the string representation of the currency as defined by | ||
/// ISO 4217, e.x. `EUR` or `GBP`. | ||
#[serde(default = "currency")] | ||
#[serde(deserialize_with = "deserialize_iso_4217")] | ||
pub currency: usize, | ||
|
||
/// The pre-authorization amount in the smallest currency unit (e.x. Cent). | ||
#[serde(default = "pre_authorization_amount")] | ||
pub pre_authorization_amount: usize, | ||
|
||
/// The default time to wait for reading a card in seconds. While a card is being read, the | ||
/// payment terminal cannot do anything else (like refunding a transaction for example). | ||
#[serde(default = "read_card_timeout")] | ||
pub read_card_timeout: u8, | ||
|
||
/// The password to the payment terminal. | ||
#[serde(default)] | ||
pub password: usize, | ||
} | ||
|
||
/// Deserializer which consumes a string code and returns the numerical code. | ||
fn deserialize_iso_4217<'de, D>(deserializer: D) -> std::result::Result<usize, D::Error> | ||
where | ||
D: serde::Deserializer<'de>, | ||
{ | ||
let code = String::deserialize(deserializer)?; | ||
iso_4217(&code).map_err(serde::de::Error::custom) | ||
} | ||
|
||
/// The default currency (returns the EUR code). | ||
const fn currency() -> usize { | ||
978 | ||
} | ||
|
||
/// The default read card timeout in seconds | ||
const fn read_card_timeout() -> u8 { | ||
15 | ||
} | ||
|
||
/// The default pre-authorization amount in Cent (returns 25 EUR). | ||
const fn pre_authorization_amount() -> usize { | ||
2500 | ||
} | ||
|
||
impl Default for FeigConfig { | ||
fn default() -> Self { | ||
Self { | ||
currency: currency(), | ||
pre_authorization_amount: pre_authorization_amount(), | ||
read_card_timeout: read_card_timeout(), | ||
password: 0, | ||
} | ||
} | ||
} | ||
|
||
/// Maps the currency code (three letters) to a numeric value. | ||
/// | ||
/// The mapping is defined under the ISO 4217. See | ||
/// https://en.wikipedia.org/wiki/ISO_4217 | ||
fn iso_4217(code: &str) -> Result<usize> { | ||
match code.to_uppercase().as_str() { | ||
// Keep the list sorted by the numeric value. | ||
"SEK" => Ok(752), | ||
"GBP" => Ok(826), | ||
"EUR" => Ok(978), | ||
_ => bail!("Unknown currency code {code}"), | ||
} | ||
} | ||
|
||
/// The configuration needed for the entire payment terminal, which contains | ||
/// parsed data. | ||
#[derive(Clone, Debug)] | ||
pub struct Config { | ||
pub terminal_id: String, | ||
/// We only use feig_serial to make sure we are connected to the proper | ||
/// terminal. | ||
pub feig_serial: String, | ||
pub ip_address: Ipv4Addr, | ||
/// Parsed from [PoleConfiguration::configuration]. | ||
pub feig_config: FeigConfig, | ||
/// Maximum number of concurrent transactions. | ||
pub transactions_max_num: usize, | ||
} | ||
|
||
impl Default for Config { | ||
fn default() -> Self { | ||
Self { | ||
terminal_id: String::default(), | ||
feig_serial: String::default(), | ||
ip_address: Ipv4Addr::new(0, 0, 0, 0), | ||
feig_config: FeigConfig::default(), | ||
transactions_max_num: 1, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_feig_config() { | ||
// Valid inputs. | ||
let empty = serde_json::from_str::<FeigConfig>("{}").unwrap(); | ||
assert_eq!(empty, FeigConfig::default()); | ||
|
||
let with_currency = serde_json::from_str::<FeigConfig>("{\"currency\": \"GBP\"}").unwrap(); | ||
assert_eq!(with_currency.currency, 826); | ||
assert_eq!(with_currency.pre_authorization_amount, 2500); | ||
|
||
let with_all = serde_json::from_str::<FeigConfig>( | ||
"{\"currency\": \"GBP\", \"pre_authorization_amount\": 10}", | ||
) | ||
.unwrap(); | ||
assert_eq!(with_all.currency, 826); | ||
assert_eq!(with_all.pre_authorization_amount, 10); | ||
|
||
// Invalid inputs. | ||
assert!(serde_json::from_str::<FeigConfig>("{\"currency\": \"ABC\"}").is_err()); | ||
assert!(serde_json::from_str::<FeigConfig>("{\"currency\": 123}").is_err()); | ||
assert!( | ||
serde_json::from_str::<FeigConfig>("{\"pre_authorization_amount\": \"AB\"}").is_err() | ||
); | ||
} | ||
} |
Oops, something went wrong.