<?php
require_once('ems_config.inc.php');

// --------------------------- [ PUBLIC INTERFACE ] ---------------------------

/*
  Parameters:
  ----------- 
  pstRequest is an array that can contain any of the following members 
  forming the request. It defaults to an empty array which uses all defaults.
  So all parameters in the array are optional.

  MsgId          [S 1..10]    Contains a unique message identifier for the
                              transaction. If not provided, it will be
                              assigned and returned on the function return arr.
                              You can create a unique MsgId by calling 
                              EMS_NewMsgId()
  MndtId         [S 1..35]    The mandate ID optionally provided by creditor
  Lang           [O]      [D] RFC 1766 language specification for the language to be
                              used in banking application. 
  ExpirationTime [I]      [D] Maximum number of minutes to wait for status
                              before it expires.
  LclInstrm      [O]      [D] Type of mandate, either CORE or B2B
  Ocrncs         [O]      [D] Type of debit:
                              FRST - First debit (Erstlastschrift)
                              RCUR - Recurring debit (Folge-Lastschrift)
                              OOFF - Onetime debit (Einmal-Lastschrift)
                              FNAL - Final debit (Letztmalige Lastschrift)
  ReturnUrl      [S]      [D] The URL where to redirect the user to
  UltmtCdtr      [A]          Ultimate creditor, if different from Creditor
                              Use same format as Cdtr in config (ems_cfg)
  UltmtDbtr      [A]          Ultimate debitor
                              Use same format as Cdtr in config (ems_cfg)
  RfrdDoc        [S 1..35]    Referred document number

  S...String
  I...Integer
  O...One of the values defined
  A...Array
  D...If not set, default from configuration file is taken

  Returns:
  --------
  array(
    'MsgId'         =>  MsgId
    'StatusRefrence'=>  Status reference to be used in EMS_Status()
    'CreDtTm'       =>  CreDtTm to be used in EMS_Status()
    'RedirectURL'   =>  On success, URL to redirect to
    'ErrorMsg'      =>  On failure, error description
    'ErrorCode'     =>  On bank error, error code
  )
*/
function EMS_Initiate($pstRequest=array())
{
    global $ems_cfg, $EMS_ret;

    $EMS_ret=array();
    $xmldata = EMS_Header('MandateServiceInitiationRequest');
    $EMS_ret['MsgId']=$pstRequest['MsgId']?$pstRequest['MsgId']:EMS_NewMsgId();
    $MsgId = sprintf("%'X-25s%s", $ems_cfg['UserID'], $EMS_ret['MsgId']);
    $time = time();
    $EMS_ret['CreDtTm'] = gmdate('Y-m-d\TH:i:s\Z');  // gmdate('c') not supported in PHP 4
    $MndtId = $pstRequest['MndtId'];
    $MndtReqId = $MndtId?$MndtId:'NOTPROVIDED';
    foreach (array('LclInstrm', 'Lang', 'ExpirationTime', 'Ocrncs', 'ReturnUrl') as $a) 
        $$a=$pstRequest[$a]?$pstRequest[$a]:$ems_cfg[$a];
    $SHA256Fingerprint=EMS_SHA256(utf8_encode($ems_cfg['PIN'].$MsgId.$EMS_ret['CreDtTm'].$MndtId.$LclInstrm.$Ocrncs.$ems_cfg['Id'].$pstRequest['RfrdDoc'].$ems_cfg['UserID']));
    $xmldata .= EMS_MsgHeader('eMandate', 'MsgHeader', $MsgId, $EMS_ret['CreDtTm']).
        "<eMandate:MandateInitiationRequest><eMandateInit:MndtInitnReq>".
        EMS_MsgHeader('eMandateInit', 'GrpHdr', $MsgId, $EMS_ret['CreDtTm'])."<eMandateInit:Mndt>";
    if ($MndtId) $xmldata .="<eMandateInit:MndtId>$MndtId</eMandateInit:MndtId>";
    $xmldata .="
<eMandateInit:MndtReqId>$MndtReqId</eMandateInit:MndtReqId>
<eMandateInit:Tp>
<eMandateInit:SvcLvl>
<eMandateInit:Cd>SEPA</eMandateInit:Cd>
</eMandateInit:SvcLvl>
<eMandateInit:LclInstrm>
<eMandateInit:Cd>$LclInstrm</eMandateInit:Cd>
</eMandateInit:LclInstrm>
</eMandateInit:Tp>
<eMandateInit:Ocrncs>
<eMandateInit:SeqTp>$Ocrncs</eMandateInit:SeqTp>
</eMandateInit:Ocrncs>
<eMandateInit:CdtrSchmeId>
<eMandateInit:Id>
<eMandateInit:PrvtId>
<eMandateInit:Othr>
<eMandateInit:Id>".$ems_cfg['Id']."</eMandateInit:Id>
<eMandateInit:SchmeNm>
<eMandateInit:Cd>SEPA</eMandateInit:Cd>
</eMandateInit:SchmeNm>
</eMandateInit:Othr>
</eMandateInit:PrvtId>
</eMandateInit:Id>
</eMandateInit:CdtrSchmeId>".EMS_Address('Cdtr', $ems_cfg['Cdtr']).EMS_Address('UltmtCdtr', $pstRequest['UltmtCdtr'])."
<eMandateInit:Dbtr/>
<eMandateInit:DbtrAgt>
<eMandateInit:FinInstnId/>
</eMandateInit:DbtrAgt>".EMS_Address('UltmtDbtr', $pstRequest['UltmtDbtr']);
    if ($pstRequest['RfrdDoc']) 
        $xmldata.="<eMandateInit:RfrdDoc><eMandateInit:Nb>".$pstRequest['RfrdDoc']."</eMandateInit:Nb></eMandateInit:RfrdDoc>";
$xmldata.="
</eMandateInit:Mndt>
</eMandateInit:MndtInitnReq>
</eMandate:MandateInitiationRequest>
<eMandate:MerchantData>
<eMandate:ReturnUrl>$ReturnUrl</eMandate:ReturnUrl>
<eMandate:Lang>$Lang</eMandate:Lang>
<eMandate:ExpirationTime>".gmdate('Y-m-d\TH:i:s\Z', $time+($ExpirationTime*60))."</eMandate:ExpirationTime>
</eMandate:MerchantData>".EMS_AuthDetails($SHA256Fingerprint)."</eMandate:MandateServiceInitiationRequest>";

//DEBUG
//	fwrite($h=fopen("test.xml", "w"), $xmldata);fclose($h); die("XML-Packet output was written to test.xml, halted.");

    return EMS_Transaction($xmldata, 'EMS_characterData', 'transinit');
}

// ----------------------------------------------------------------------------

/*
  EMS_Status checks the status of a transaction.

  Parameters:
  -----------
  pstRequest is basically the array you receive as result from
  EMS_Init, it has to contain the following members:

  array(
    'MsgId'         =>  MsgId from EMS_Initiate()
    'StatusRefrence'=>  Status reference from EMS_Initiate()
    'CreDtTm'       =>  CreDtTm from EMS_Initiate()
  )

  Returns:
  --------
  array(
    'ErrorMsg'      =>  On execution failure, error description
    'ErrorCode'     =>  On bank error, error code
    'Status'        =>  The status of the transaction, one of: OK, NOK, UNKNOWN
  )
*/
function EMS_Status($pstRequest)
{
    global $ems_cfg, $EMS_ret;

    $EMS_ret=array();
    $MsgId = sprintf("%'X-25s%s", $ems_cfg['UserID'], $pstRequest['MsgId']);
    $SHA256Fingerprint = EMS_SHA256(utf8_encode($ems_cfg['PIN'].$MsgId.$pstRequest['CreDtTm'].
        $pstRequest['StatusReference'].$ems_cfg['UserID']));
    $xmldata = EMS_Header('MandateServiceStatusRequest').
        EMS_MsgHeader('eMandate', 'MsgHeader', $MsgId, $pstRequest['CreDtTm']).
        '<eMandate:StatusReference>'.$pstRequest['StatusReference'].'</eMandate:StatusReference>'.
        EMS_AuthDetails($SHA256Fingerprint).'</eMandate:MandateServiceStatusRequest>';

    return EMS_Transaction($xmldata, 'EMS_characterDataStatus', 'statusrequest');
}

// ----------------------------------------------------------------------------

/* 
   This is an example for creating a relatively unique new message ID
   with 10 chars.
   Of course you can use your own functions instead.
 */
function EMS_NewMsgId()
{
   return sprintf('%02X%08X',rand(0,255),time());
}

// --------------------------- [ STATIC FUNCTIONS ] ---------------------------

function EMS_Transaction($xmldata, $datahandler, $reqtyp)
{
    global $currentTag, $EMS_ret;

	unset($currentTag);
	$xml_parser = xml_parser_create('UTF-8');
    xml_set_element_handler($xml_parser, 'EMS_startElement', 'EMS_endElement');
    xml_set_character_data_handler($xml_parser, $datahandler);

	if (!xml_parse($xml_parser, EMS_SendRequest($xmldata, $reqtyp)))
	    $EMS_ret['ErrorMsg']=sprintf("XML error: %s in line %d",xml_error_string(xml_get_error_code($xml_parser)),xml_get_current_line_number($xml_parser));
	xml_parser_free($xml_parser);

	return $EMS_ret;
}

function EMS_PutLog($text)
{
  global $ems_cfg;

  if ($ems_cfg['Logfile']) {
    fputs(($fp=fopen($ems_cfg['Logfile'],"a")),strftime("um: %d.%m.%Y %H:%M:%S",time()).": $text\n-------------------------------------------------------------------------------\n");
    fclose($fp);
  }
}

function EMS_Header($Tag)
{
    $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>".
    "<eMandate:$Tag ".
    "xmlns:eMandate=\"http://www.stuzza.at/namespaces/eMandate/2013\" ";
    if ($Tag=='MandateServiceInitiationRequest') $ret.="xmlns:eMandateInit=\"urn:iso:std:iso:20022:tech:xsd:pain.009.001.02\" ";
    return $ret.
    "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ".
    "xsi:schemaLocation=\"http://www.stuzza.at/namespaces/eMandate/2013/eMandateService_v1.0.xsd\">";
}

function EMS_MsgHeader($NS, $Tag, $MsgId, $CreDtTm)
{
    return "<$NS:$Tag><$NS:MsgId>$MsgId</$NS:MsgId><$NS:CreDtTm>$CreDtTm</$NS:CreDtTm></$NS:$Tag>";
}

function EMS_AuthDetails($SHA256Fingerprint)
{
    global $ems_cfg;

    return "<eMandate:AuthenticationDetails><eMandate:UserId>".$ems_cfg['UserID']."</eMandate:UserId>".
        "<eMandate:SHA256Fingerprint>$SHA256Fingerprint</eMandate:SHA256Fingerprint></eMandate:AuthenticationDetails>";
}

function EMS_Address($sTag, $aI)
{
    if (!$aI) return '';
    $ret = "<eMandateInit:$sTag>";
    if ($aI['Nm']) $ret.='<eMandateInit:Nm>'.$aI['Nm'].'</eMandateInit:Nm>';
    if ($aI['Ctry'] || $aI['AdrLine'][0])
    {
        $ret.='<eMandateInit:PstlAdr>';
        if ($aI['Ctry']) $ret.='<eMandateInit:Ctry>'.$aI['Ctry'].'</eMandateInit:Ctry>';
        foreach ($aI['AdrLine'] as $a) $ret.="<eMandateInit:AdrLine>$a</eMandateInit:AdrLine>";
        $ret.='</eMandateInit:PstlAdr>';
    }
    return $ret."</eMandateInit:$sTag>";
}

function EMS_SHA256($str)
{
    if (version_compare(PHP_VERSION,'5.1.2','>='))
        return hash('sha256', $str);
    if (function_exists('mhash') && defined('MHASH_SHA256'))
        return base64_encode(bin2hex(mhash(MHASH_SHA256, $str)));
    if (function_exists('sha256'))
        return sha256($str);
    else 
    {
        include ('ems_php4_sha256.inc.php');
        return strtoupper($hexStr);
    }
}

function EMS_SendRequest($xmldata, $reqtyp)
{
    global $ems_cfg;

    $Url='https://routing.e-mandat.at/appl/eMandatSO'.($ems_cfg['System']?'-'.$ems_cfg['System']:'').'/'.$reqtyp.'/ems/v1_0';
    EMS_PutLog("(Request to $Url) Request packet:\n$xmldata");

    // Setup CURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $Url);
    curl_setopt($ch, CURLOPT_POST,1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, utf8_encode($xmldata));
    curl_setopt($ch, CURLOPT_TIMEOUT, 60);
    curl_setopt($ch, CURLOPT_HEADER,0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/xml"));

    // Abfrage durchfuehren
    $result=curl_exec ($ch);
    EMS_PutLog("(Request to EMS-Server) Response:\n$result");
    curl_close ($ch);
    return $result;
}

function EMS_startElement($parser, $name, $attrs)
{
    global $currentTag;
    $currentTag = $name;
}

function EMS_endElement($parser, $name)
{
    global $currentTag;
    $currentTag = "";
}

function EMS_characterData($parser, $data)
{
    global $currentTag, $EMS_ret;

    list($prefix, $tag)=explode(":", $currentTag, 2);
    if (!$tag) $tag=$currentTag;
    switch(strtoupper($tag))
    {
        case "ERRORCODE": $EMS_ret['ErrorCode']=$data; break;
        case "MESSAGE": $EMS_ret['ErrorMsg']=$data; break;
        case "REDIRECTURL": $EMS_ret['RedirectURL'].=html_entity_decode($data); break;
        case "STATUSREFERENCE": $EMS_ret['StatusReference']=$data; break;
    }
}

function EMS_characterDataStatus($parser, $data)
{
	global $currentTag, $EMS_ret;

	list($prefix, $tag)=explode(":", $currentTag, 2);
	if (!$tag) $tag=$currentTag;
    switch(strtoupper($tag))
    {
	    case "STATUS": $EMS_ret['Status']=$data; break;
        case "ERRORCODE": $EMS_ret['ErrorCode']=$data; break;
        case "MESSAGE": $EMS_ret['ErrorMsg']=$data; break;
    }
}
