Skip to content

Conversation

@dorezyuk
Copy link
Contributor

@dorezyuk dorezyuk commented Jan 21, 2026

Currently we run init and diagnostics on every start up - until the successfully_configure flag is set. While being a simple logic this results in lots of unnecessary transactions (inti and diagnostics are transactions from the view of the payment provider backend and 1. cost money and 2. can overload the payment provider backend).

The idea is to query the payment terminal (status inquiry, which is fast and does not result in calls to the payment provider backend) and based on the return code to reason if we need to run the aforementioned commands.

The status inquiry returns the terminal_status_code, which is defined under chapter 11 of zvt.

Dima Dorezyuk added 2 commits January 21, 2026 17:22
Signed-off-by: Dima Dorezyuk <ddo@qwello.eu>
Signed-off-by: Dima Dorezyuk <ddo@qwello.eu>
@dorezyuk dorezyuk force-pushed the feat/update_configure_logic branch from 7d4e408 to a173fd5 Compare January 23, 2026 07:40
#[error("TID not activated")]
/// Note: This does **not** mean that we have to activate the tid on the
/// payment terminal side.
TidNotActivated = 0x3a,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this has been verified with lavego

/// Messages as defined under chapter 11.
#[derive(Debug, PartialEq, FromPrimitive, Clone, Copy, Error)]
#[repr(u8)]
pub enum TerminalStatusCode {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we act on only a fraction of the error codes right now - but we would need to observe which ones we really see from feig

constants::TerminalStatusCode::PtReady => {
log::debug!("Terminal is ready");
}
constants::TerminalStatusCode::ReconciliationRequired => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

note - this is not identical with the emails we receive regarding eod jobs - this would only fire if the pt itself thinks we need to run eod

Copy link

@hovinen hovinen left a comment

Choose a reason for hiding this comment

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

I might need you to walk me through some of these changes, since I'm not so familiar with all of the components involved.

}

impl FromStr for StatusType {
type Err = String;
Copy link

Choose a reason for hiding this comment

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

Given that this project is using anyhow anyway, would it make sense to use anyhow::Error here?

match s.to_lowercase().as_str() {
"feig" => Ok(StatusType::Feig),
"zvt" => Ok(StatusType::Zvt),
_ => Err(format!("'{}' is not a valid StatusType (feig | zvt)", s)),
Copy link

Choose a reason for hiding this comment

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

You could inline s in the format string: {s}. I see this elsewhere so I guess there's no concern about supporting older rustc version which don't have that yet.

pub async fn configure(&mut self) -> Result<()> {
if self.successfully_configured {
return Ok(());
// Get the status inquiry so we can reason on the terminal_status_code.
Copy link

Choose a reason for hiding this comment

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

It took me a while to understand this function. It seems that it's doing two distinct things: fetching the terminal status code via a kind of RPC and evaluating that to possibly issue some commands. What would you say about extracting those parts into separate, appropriately named functions to make the steps clearer?

match response? {
CVendFunctionsEnhancedSystemInformationCompletion(data) => log::info!("{data:#?}"),
Abort(_) => bail!("Failed to get system info. Received Abort."),
async fn status(
Copy link

Choose a reason for hiding this comment

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

I'm having trouble understanding how these changes here map to what's in the PR description. This seems to be modifying the CLI tool to log some data in response to a command. How does that relate to the rest of the changes?

StatusType::Zvt => {
let request = packets::StatusEnquiry {
password: Some(password),
service_byte: service_byte,
Copy link

Choose a reason for hiding this comment

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

As discussed: since this can cause deserialization to fail for certain values, consider detecting those cases and printing at least a warning.

SystemError = 0xff,
}

/// Messages as defined under chapter 11.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit - to make the comment clearer, maybe add:
/// Messages as defined under chapter 11 of the ZVT specifications, under "Terminal Status Codes".

CompletionData(packets::CompletionData),
/// 2.55.5. The possible tags in the status enquiry correspond to the
/// ReceiptPrintoutCompletion.
CompletionData(packets::ReceiptPrintoutCompletion),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure if I understand - until now did we misread status responses?

};

let mut stream = sequences::StatusEnquiry::into_stream(&request, socket);
while let Some(response) = stream.next().await {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we also get an Abort here from the stream? Would we likewise want to bail if we get it?

/// We're doing the following based on the terminal status code:
/// * If PtReady - return
/// * If ReconciliationRequired - run end-of-day
/// * If InitialisationRequired or DiagnosisRequired - set terminal id and run emv diagnostics
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's incomplete here - If InitialisationRequired or DiagnosisRequired or TerminalActivationRequired - set terminal id and run emv diagnostics, and initialize the terminal.


match status {
constants::TerminalStatusCode::PtReady => {
log::debug!("Terminal is ready");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit - I think this can be info, since this only happens on new and reconnect

terminal_status_code = Some(completion_data.terminal_status_code);
}
other => {
log::debug!("{other:#?}");
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this is unexpected behavior, I think this can likewise be info / warning.

}
constants::TerminalStatusCode::ReconciliationRequired => {
info!("Reconciliation required, running end of day");
self.end_of_day().await?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We wouldn't know a failure occurred here, since end_of_day doesn't log failure. Do we want to add some failure log there, here, or log the error when calling configure? Same goes for failure of set_terminal_id, initialize, and run_diagnosis below.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Better yet - since if this function fails we won't have a working PT on a pole, better log it in the calling function and add an alert for this failure to track it

self.set_terminal_id().await?;
self.run_diagnosis(packets::DiagnosisType::EmvConfiguration)
.await?;
self.initialize().await?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit - We can safely return after this line, we don't need to check if the TID was changed again since we've just set it.

Copy link

@hovinen hovinen left a comment

Choose a reason for hiding this comment

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

LGTM based on our discussion yesterday. Please see also my comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants