Skip to content
Open
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
41 changes: 41 additions & 0 deletions zvt/src/packets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,47 @@ pub struct Registration {
pub tlv: Option<tlv::Registration>,
}

#[derive(Debug, Default, PartialEq, Zvt)]
#[zvt_control_field(class = 0x06, instr = 0x01)]
pub struct Authorization {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is mostly the same as reservation - but we don't have a nice flatten option as in serde so right now there is probably no good way around copy and pasting it

#[zvt_bmp(number = 0x04, length = length::Fixed<6>, encoding = encoding::Bcd)]
pub amount: Option<usize>,

#[zvt_bmp(number = 0x49, length = length::Fixed<2>, encoding = encoding::Bcd)]
pub currency: Option<usize>,

#[zvt_bmp(number = 0x19)]
pub payment_type: Option<u8>,

#[zvt_bmp(number = 0x0e, length = length::Fixed<2>, encoding = encoding::Bcd)]
pub expiry_date: Option<usize>,

#[zvt_bmp(number = 0x22, length = length::Llv, encoding = encoding::Bcd)]
pub card_number: Option<usize>,

#[zvt_bmp(number = 0x23, length = length::Llv, encoding= encoding::Hex)]
pub track_2_data: Option<String>,

// Unclear how to interpret this.
#[zvt_bmp(number = 0x01)]
pub timeout: Option<u8>,

#[zvt_bmp(number = 0x02)]
pub maximum_no_of_status_info: Option<u8>,

#[zvt_bmp(number = 0x05)]
pub pump_no: Option<u8>,

#[zvt_bmp(number = 0x3c, length = length::Lllv)]
pub additional_text: Option<String>,

#[zvt_bmp(number = 0x8a)]
pub zvt_card_type: Option<u8>,

#[zvt_bmp(number = 0x06, length = length::Tlv)]
pub tlv: Option<tlv::AuthorizationData>,
}

#[derive(Debug, Default, PartialEq, Eq, Zvt)]
#[zvt_control_field(class = 0x06, instr = 0x0f)]
pub struct CompletionData {
Expand Down
12 changes: 12 additions & 0 deletions zvt/src/packets/tlv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ pub struct PreAuthData {
pub bmp_data: Option<Bmp60>,
}

#[derive(Debug, Default, PartialEq, Zvt)]
pub struct AuthorizationData {
#[zvt_tlv(tag = 0x41, encoding = encoding::Hex)]
pub card_type: Option<String>,

#[zvt_tlv(tag = 0x43, encoding = encoding::Hex)]
pub application_id: Option<String>,

#[zvt_tlv(tag = 0x1F15)]
pub card_reading_control: Option<u8>,
}

#[derive(Debug, PartialEq, Zvt)]
pub struct Diagnosis {
#[zvt_tlv(tag = 0x1b)]
Expand Down
81 changes: 59 additions & 22 deletions zvt/src/sequences.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,65 @@ impl Sequence for Registration {
type Output = RegistrationResponse;
}

/// Authorization sequence as defined under 2.1.
///
/// Using the command Authorization the ECR initiates a payment transaction.
pub struct Authorization;

/// Response to [packets::Reservation] message, as defined under 2.8.
///
/// The response is the same as for Authorization, defined in chapter 2.1.
#[derive(Debug, ZvtEnum)]
#[allow(clippy::large_enum_variant)]
pub enum AuthorizationResponse {
/// 2.2.4
IntermediateStatusInformation(packets::IntermediateStatusInformation),
// 2.2.5 produces no message.
/// 2.2.6
StatusInformation(packets::StatusInformation),
/// 2.2.7
PrintLine(packets::PrintLine),
/// 2.2.7
PrintTextBlock(packets::PrintTextBlock),
// 2.2.8 produces no message.
/// 2.2.9
CompletionData(packets::CompletionData),
/// 2.2.9
Abort(packets::Abort),
}

impl Sequence for Authorization {
type Input = packets::Authorization;
type Output = AuthorizationResponse;

fn into_stream<'a, Source>(
input: &'a Self::Input,
src: &'a mut PacketTransport<Source>,
) -> Pin<Box<dyn Stream<Item = Result<Self::Output>> + Send + 'a>>
where
Source: AsyncReadExt + AsyncWriteExt + Unpin + Send,
Self: 'a,
{
let s = try_stream! {
// 2.1
src.write_packet_with_ack(input).await?;

loop {
let packet = src.read_packet().await?;
src.write_packet(&packets::Ack {}).await?;
match packet {
AuthorizationResponse::CompletionData(_) | AuthorizationResponse::Abort(_) => {
yield packet;
break;
}
_ => yield packet,
}
}
};
Box::pin(s)
}
}

/// Read-card sequence as defined under 2.21.
///
/// With this command the PT reads a chip-card/magnet-card and transmits the
Expand Down Expand Up @@ -348,28 +407,6 @@ impl Sequence for EndOfDay {
/// amount via a [PartialReversal] or Book Total (06 24, not implemented).
pub struct Reservation;

/// Response to [packets::Reservation] message, as defined under 2.8.
///
/// The response is the same as for Authorization, defined in chapter 2.1.
#[derive(Debug, ZvtEnum)]
#[allow(clippy::large_enum_variant)]
pub enum AuthorizationResponse {
/// 2.2.4
IntermediateStatusInformation(packets::IntermediateStatusInformation),
// 2.2.5 produces no message.
/// 2.2.6
StatusInformation(packets::StatusInformation),
/// 2.2.7
PrintLine(packets::PrintLine),
/// 2.2.7
PrintTextBlock(packets::PrintTextBlock),
// 2.2.8 produces no message.
/// 2.2.9
CompletionData(packets::CompletionData),
/// 2.2.9
Abort(packets::Abort),
}

impl Sequence for Reservation {
type Input = packets::Reservation;
type Output = AuthorizationResponse;
Expand Down
44 changes: 44 additions & 0 deletions zvt_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum SubCommands {
Status(StatusArgs),
FactoryReset(FactoryResetArgs),
Registration(RegistrationArgs),
Authorization(AuthorizationArgs),
SetTerminalId(SetTerminalIdArgs),
Initialization(InitializationArgs),
Diagnosis(DiagnosisArgs),
Expand Down Expand Up @@ -50,6 +51,23 @@ struct RegistrationArgs {
config_byte: u8,
}

#[derive(FromArgs, PartialEq, Debug)]
/// Do payment using authorization command.
#[argh(subcommand, name = "pay")]
struct AuthorizationArgs {
/// amount to pay (in cents).
#[argh(positional)]
amount: usize,

/// currency code. Defauls to 978 (= EUR).
#[argh(option, default = "978")]
currency_code: usize,

/// payment type. Defaults to 0 .
#[argh(option, default = "0")]
payment_type: u8,
}

#[derive(FromArgs, PartialEq, Debug)]
/// Sets the terminal id.
#[argh(subcommand, name = "set_terminal_id")]
Expand Down Expand Up @@ -294,6 +312,31 @@ async fn registration(
Ok(())
}

async fn authorization(
socket: &mut PacketTransport,
args: &AuthorizationArgs,
) -> Result<()> {
let request = packets::Authorization {
amount: Some(args.amount),
currency: Some(args.currency_code),
payment_type: Some(args.payment_type),
..Default::default()
};

let mut stream = sequences::Authorization::into_stream(&request, socket);
use sequences::AuthorizationResponse::*;
while let Some(response) = stream.next().await {
match response? {
IntermediateStatusInformation(_) | CompletionData(_) => (),
PrintLine(data) => log::info!("{}", data.text),
PrintTextBlock(data) => log::info!("{data:#?}"),
Abort(data) => bail!("Received Abort: {:?}", data),
StatusInformation(data) => log::info!("StatusInformation: {:#?}", data),
}
}
Ok(())
}

async fn set_terminal_id(
socket: &mut PacketTransport,
password: usize,
Expand Down Expand Up @@ -545,6 +588,7 @@ async fn main() -> Result<()> {
SubCommands::Status(_) => status(&mut socket).await?,
SubCommands::FactoryReset(_) => factory_reset(&mut socket, args.password).await?,
SubCommands::Registration(a) => registration(&mut socket, args.password, &a).await?,
SubCommands::Authorization(a) => authorization(&mut socket, &a).await?,
SubCommands::SetTerminalId(a) => set_terminal_id(&mut socket, args.password, &a).await?,
SubCommands::Initialization(_) => initialization(&mut socket, args.password).await?,
SubCommands::Diagnosis(a) => diagnosis(&mut socket, &a).await?,
Expand Down