Skip to content

Instantly share code, notes, and snippets.

@t-oster
Created April 18, 2018 06:53
Show Gist options
  • Save t-oster/a1bed2bb82d566bbdb90706d521a8f83 to your computer and use it in GitHub Desktop.
Save t-oster/a1bed2bb82d566bbdb90706d521a8f83 to your computer and use it in GitHub Desktop.
Little helper class to request a payment on an OPI enabled Card Terminal
<?php
/**
* Little helper class to request a payment on an OPI enabled Card Terminal
*/
class OPIHelper {
private $ip;
private $port;
private $requestID = 0;
private function requestID()
{
return $this->requestID++;
}
private function loginXML()
{
return "
<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>
<ServiceRequest RequestType=\"Login\" WorkstationID=\"0\" RequestID=\"".$this->requestID()."\">
<POSdata>
<POSTimeStamp>".date("c")."</POSTimeStamp>
</POSdata>
</ServiceRequest>";
}
private function reconciliationXML()
{
return "
<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>
<ServiceRequest RequestType=\"Reconciliation\" WorkstationID=\"0\" RequestID=\"".$this->requestID()."\">
<POSdata>
<POSTimeStamp>".date("c")."</POSTimeStamp>
</POSdata>
</ServiceRequest>";
}
private function logoutXML()
{
return "
<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>
<ServiceRequest RequestType=\"Logoff\" WorkstationID=\"0\" RequestID=\"".$this->requestID()."\">
<POSdata>
<POSTimeStamp>".date("c")."</POSTimeStamp>
</POSdata>
</ServiceRequest>";
}
private function cardPaymentXML($amount)
{
return "
<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>
<CardServiceRequest RequestType=\"CardPayment\" WorkstationID=\"0\" RequestID=\"".$this->requestID()."\">
<POSdata>
<POSTimeStamp>".date("c")."</POSTimeStamp>
</POSdata>
<TotalAmount>$amount</TotalAmount>
</CardServiceRequest>";
}
private function ticketReprintXML($originalTransaction = null)
{
return "
<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>
<CardServiceRequest RequestType=\"TicketReprint\" WorkstationID=\"0\" RequestID=\"".$this->requestID()."\">
<POSdata>
<POSTimeStamp>".date("c")."</POSTimeStamp>
</POSdata>
</CardServiceRequest>";
//TODO: <OriginalTransaction TerminalID="" TerminalBatch="" STAN="" TimeStamp""/>
}
private function receiveXML($f)
{
$length = unpack("Ni", fread($f, 4));
$data = fread($f, $length["i"]);
return $data;
}
private function sendXML($data, $f)
{
fwrite($f, pack("N", strlen($data)));
fwrite($f, $data);
return $this->receiveXML($f);
}
public function __construct($ip = "10.10.10.212", $port = 20002)
{
$this->ip = $ip;
$this->port = $port;
}
public function requestMoney($amount)
{
$result = false;
$f = fsockopen($this->ip, $this->port);
$data = $this->sendXML($this->loginXML(), $f);
if (strpos($data, 'OverallResult="Success"') === false)
{
echo "Error during login.";
echo $data;
fclose($f);
return false;
}
$data = $this->sendXML($this->cardPaymentXML($amount), $f);
if (strpos($data, 'OverallResult="Aborted"') !== false)
{
//aborted...
}
else if (strpos($data, 'OverallResult="Success"') !== false)
{
$result = true;
}
$data = $this->sendXML($this->logoutXML(), $f);
if (strpos($data, 'OverallResult="Success"') === false)
{
echo "Error during logout.";
echo $data;
}
fclose($f);
return $result;
}
public function reprintTicket()
{
$result = false;
$f = fsockopen($this->ip, $this->port);
$data = $this->sendXML($this->loginXML(), $f);
if (strpos($data, 'OverallResult="Success"') === false)
{
echo "Error during login.";
echo $data;
fclose($f);
return false;
}
$data = $this->sendXML($this->ticketReprintXML(), $f);
if (strpos($data, 'OverallResult="Success"') !== false)
{
$result = true;
}
$data = $this->sendXML($this->logoutXML(), $f);
if (strpos($data, 'OverallResult="Success"') === false)
{
echo "Error during logout.";
echo $data;
}
fclose($f);
return $result;
}
public function listPayments()
{
$result = false;
$f = fsockopen($this->ip, $this->port);
$data = $this->sendXML($this->loginXML(), $f);
if (strpos($data, 'OverallResult="Success"') === false)
{
echo "Error during login.";
echo $data;
fclose($f);
return false;
}
$data = $this->sendXML($this->reconciliationXML(), $f);
$xml = new SimpleXMLElement($data);
if ($xml["OverallResult"] == "Success")
{
$result = array();
foreach ($xml->Reconciliation as $r)
{
foreach ($r->TotalAmount as $ta)
{
$r = array();
foreach (array("NumberPayments","PaymentType","Currency","CardCircuit","Acquirer") as $field)
{
if (isset($ta[$field]) && $ta[$field] !== "")
{
$r[$field] = $ta[$field];
}
}
$result []= $r;
}
}
}
$data = $this->sendXML($this->logoutXML(), $f);
if (strpos($data, 'OverallResult="Success"') === false)
{
echo "Error during logout.";
echo $data;
}
fclose($f);
return $result;
}
}
@Niksac
Copy link

Niksac commented Nov 1, 2018

Hi, i tried your GIST but my ingenico POS terminal reacts strangely to the requests I made. Can you give a hint where to find the O.P.I. specifications? It does not seem too open to me .

@t-oster
Copy link
Author

t-oster commented Apr 7, 2020

They are not very open, you have to sign a contract in order to get them and I am not even sure I am allowed to share this code

@SVSwift
Copy link

SVSwift commented Apr 7, 2020

Just write to Wincor-Nixdorf to get the specification. You'd have to sign a NDA.

@oliverschaefer
Copy link

@Niksac @t-oster The terminal reacts strangely since the script only covers one communication channel (from pc to terminal and back). In order for the payment to be successful, you need the second channel (from terminal to pc and back) as well. The corresponding section of the specification is DeviceRequest and DeviceResponse

@samikarak
Copy link

@Niksac @t-oster The terminal reacts strangely since the script only covers one communication channel (from pc to terminal and back). In order for the payment to be successful, you need the second channel (from terminal to pc and back) as well. The corresponding section of the specification is DeviceRequest and DeviceResponse

@oliverschaefer does this mean, that there needs to be 2 open channels at the same time? In which order are the requests send? In which order are the responses received? can you elaborate a little more on you suggestion this would help me out a lot. Thank you!

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